viewres.c revision fffe5102
1/*
2 * $XConsortium: viewres.c,v 1.74 94/04/17 20:43:24 converse Exp $
3 *
4 *
5Copyright (c) 1989  X Consortium
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall not be
25used in advertising or otherwise to promote the sale, use or other dealings
26in this Software without prior written authorization from the X Consortium.
27 * *
28 * Author:  Jim Fulton, MIT X Consortium
29 */
30/* $XFree86: xc/programs/viewres/viewres.c,v 1.6 2003/05/27 22:26:58 tsi Exp $ */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <X11/StringDefs.h>
35#include <X11/IntrinsicP.h>
36#include <X11/Xaw/Cardinals.h>
37#include <X11/Xaw/Box.h>
38#include <X11/Xaw/Form.h>
39#include <X11/Xaw/Command.h>
40#include <X11/Xaw/MenuButton.h>
41#include <X11/Xaw/SimpleMenu.h>
42#include <X11/Xaw/Sme.h>
43#include <X11/Xaw/SmeBSB.h>
44#include <X11/Xaw/SmeLine.h>
45#include <X11/Xaw/Paned.h>
46#include <X11/Xaw/Porthole.h>
47#include <X11/Xaw/Toggle.h>
48#include <X11/Xaw/Text.h>
49#include <X11/Xaw/List.h>
50#include <X11/Xaw/Scrollbar.h>
51#include <X11/Xaw/Panner.h>
52#include <X11/Xaw/Tree.h>
53#include <X11/Xmu/Converters.h>
54#include <X11/Xmu/CharSet.h>
55#include <X11/Xmu/WidgetNode.h>
56#include <X11/Xaw/AllWidgets.h>
57
58#define widget_list XawWidgetArray  /* or motif or ol or ... */
59#define nwidgets XawWidgetCount
60
61typedef struct {
62    const char **resource_labels;	/* names of res added by widget */
63    Cardinal nnewresources;		/* number res added by widget */
64    Cardinal nnewconstraints;		/* number res added by widget */
65    Cardinal nnew;			/* number new */
66    Widget instance;			/* Label widget in box in tree */
67    Widget resource_lw;			/* List widget showing resources */
68    int selection_index;		/* -1 or index into selection_list */
69} ViewresData;
70
71#define VData(node) ((ViewresData *) (node)->data)
72
73
74#define IsShowing(node) (VData(node)->resource_lw && \
75			 XtIsManaged(VData(node)->resource_lw))
76
77
78struct WidgetList {
79    int n_elements;
80    int max_elements;
81    XmuWidgetNode **elements;
82};
83
84static struct WidgetList selected_list = { 0, 0, (XmuWidgetNode **) NULL };
85
86#define INSERT_NODE(node,i) \
87  selected_list.elements[VData(node)->selection_index = (i)] = (node)
88
89#define REMOVE_NODE(node) \
90  selected_list.elements[VData(node)->selection_index] = \
91  (XmuWidgetNode *) NULL; VData(node)->selection_index = (-1)
92
93static const char *ProgramName;
94static int NumberShowing = 0;
95
96static Arg sensitiveargs[2] = {{ XtNsensitive, (XtArgVal) FALSE },
97			       { XtNsensitive, (XtArgVal) TRUE }};
98
99static const char *help_message[] = {
100    "-top name        object to be top of tree",
101    "-variable        show variable name instead of class name",
102    "-vertical        list the tree vertically",
103    NULL
104};
105
106static XrmOptionDescRec Options[] = {
107    { "-top", "*topObject", XrmoptionSepArg, (XPointer) NULL },
108    { "-variable", "*showVariable", XrmoptionNoArg, (XPointer) "on" },
109    { "-vertical", "*Tree.Gravity", XrmoptionNoArg, (XPointer) "north" }
110};
111
112
113typedef struct {
114    char *top_object;
115    Boolean show_variable;
116} OptionsRec;
117
118static OptionsRec options;
119
120#define Offset(field) XtOffsetOf(OptionsRec, field)
121
122static XtResource Resources[] = {
123    { "topObject", "TopObject", XtRString, sizeof(char *),
124	Offset(top_object), XtRString, (XtPointer) "object" },
125    { "showVariable", "ShowVariable", XtRBoolean, sizeof(Boolean),
126	Offset(show_variable), XtRImmediate, (XtPointer) FALSE },
127};
128
129#undef Offset
130
131static const char *fallback_resources[] = {
132    "*allowShellResize: true",
133    "*Porthole.top: ChainTop",
134    "*Porthole.left: ChainLeft",
135    "*Porthole.bottom: ChainBottom",
136    "*Porthole.right:  ChainRight",
137    "*Porthole.resizable: on",
138    "*Panner.top: ChainTop",
139    "*Panner.left: ChainLeft",
140    "*Panner.bottom: ChainTop",
141    "*Panner.right:  ChainLeft",
142    "*Panner.resizable: on",
143    "*Tree*ShapeStyle: rectangle",
144    "*Tree*Toggle*BorderWidth: 0",
145    "*Porthole*Box.BorderWidth: 0",
146    "*Porthole*Box.HSpace: 0",
147    "*Porthole*Box.VSpace: 0",
148    "*Paned*allowResize: true",
149    "*buttonbox.quit.Translations:  #override \\n <Btn1Down>,<Btn1Up>: Quit() unset()",
150    "*Toggle.Translations: #augment \\n <Btn2Down>,<Btn2Up>: set() notify() Resources(toggle)",
151    NULL
152};
153
154static void ActionQuit(Widget, XEvent *, String *, Cardinal *);
155static void ActionSetLableType(Widget, XEvent *, String *, Cardinal *);
156static void ActionSetOrientation(Widget, XEvent *, String *, Cardinal *);
157static void ActionSelect(Widget, XEvent *, String *, Cardinal *);
158static void ActionResources(Widget, XEvent *, String *, Cardinal *);
159static void set_labeltype_menu(Boolean, Boolean);
160static void set_orientation_menu(XtGravity, Boolean);
161static void build_tree(XmuWidgetNode *, Widget, Widget);
162static void set_node_labels(XmuWidgetNode *, int);
163
164static XtActionsRec viewres_actions[] = {
165    { "Quit", ActionQuit },
166    { "SetLabelType", ActionSetLableType },
167    { "SetOrientation", ActionSetOrientation },
168    { "Select", ActionSelect },
169    { "Resources", ActionResources },
170};
171
172static Atom wm_delete_window;
173
174#define BOOL_OFF 0
175#define BOOL_ON 1
176#define BOOL_TOGGLE 2
177
178#define VIEW_HORIZONTAL 0
179#define VIEW_VERTICAL 1
180#define VIEW_VARIABLES 2
181#define VIEW_CLASSES 3
182#define VIEW_SHOW_RESOURCES 4
183#define VIEW_HIDE_RESOURCES 5
184#define VIEW_number 6
185
186#define SELECT_NOTHING 0
187#define SELECT_ALL 1
188#define SELECT_INVERT 2
189#define SELECT_PARENT 3
190#define SELECT_ANCESTORS 4
191#define SELECT_CHILDREN 5
192#define SELECT_DESCENDANTS 6
193#define SELECT_HAS_RESOURCES 7
194#define SELECT_SHOWN_RESOURCES 8
195#define SELECT_number 9
196
197static struct _nametable {
198    const char *name;
199    int value;
200} select_nametable[] = {
201    { "nothing", SELECT_NOTHING },
202    { "all", SELECT_ALL },
203    { "invert", SELECT_INVERT },
204    { "parent", SELECT_PARENT },
205    { "ancestors", SELECT_ANCESTORS },
206    { "children", SELECT_CHILDREN },
207    { "descendants", SELECT_DESCENDANTS },
208    { "resources", SELECT_HAS_RESOURCES },
209    { "shown", SELECT_SHOWN_RESOURCES },
210}, boolean_nametable[] = {
211    { "off", BOOL_OFF },
212    { "false", BOOL_OFF },
213    { "no", BOOL_OFF },
214    { "on", BOOL_ON },
215    { "true", BOOL_ON },
216    { "yes", BOOL_ON },
217    { "toggle", BOOL_TOGGLE },
218};
219
220static Widget treeWidget;
221static Widget viewButton, viewMenu, selectButton, selectMenu;
222static Widget view_widgets[VIEW_number];
223static Widget select_widgets[SELECT_number];
224static XmuWidgetNode *topnode;
225
226static Arg false_args[1] = {{ XtNstate, (XtArgVal) FALSE }};
227static Arg true_args[1] = {{ XtNstate, (XtArgVal) TRUE }};
228
229
230/*
231 * routines
232 */
233static void
234usage (void)
235{
236    const char **cpp;
237    fprintf (stderr, "usage:  %s [-options...]\n", ProgramName);
238    fprintf(stderr, "\nwhere options include:\n");
239    for (cpp = help_message; *cpp; cpp++) {
240	fprintf (stderr, "    %s\n", *cpp);
241    }
242    fprintf(stderr, "\n");
243    exit (1);
244}
245
246
247static XmuWidgetNode *
248widget_to_node (Widget gw)
249{
250    XmuWidgetNode *node;
251    int i;
252
253    if (XtIsSubclass (gw, toggleWidgetClass)) {
254	for (i = 0, node = widget_list; i < nwidgets; i++, node++) {
255	    if (VData(node)->instance == gw) return node;
256	}
257    } else if (XtIsSubclass (gw, listWidgetClass)) {
258	for (i = 0, node = widget_list; i < nwidgets; i++, node++) {
259	    if (VData(node)->resource_lw == gw) return node;
260	}
261    }
262    return (XmuWidgetNode *) NULL;
263}
264
265
266static void
267initialize_widgetnode_list (XmuWidgetNode ***listp, int *sizep, int n)
268{
269    int i;
270    XmuWidgetNode **l;
271
272    if (!*listp) {
273        *listp = (XmuWidgetNode **)
274	  XtCalloc ((unsigned int) n, (unsigned int)sizeof(XmuWidgetNode **));
275        *sizep = ((*listp) ? n : 0);
276        return;
277    }
278    if (n > *sizep) {
279        *listp = (XmuWidgetNode **) XtRealloc ((char *) *listp,
280					       (unsigned int)
281					       (n * sizeof(XmuWidgetNode **)));
282	if (!*listp) {
283	    *sizep = 0;
284	    return;
285	}
286	for (i = *sizep, l = (*listp) + i; i < n; i++, l++) *l =
287	  (XmuWidgetNode *) NULL;
288	*sizep = n;
289    }
290    return;
291}
292
293
294static Boolean
295set_resource_labels (XmuWidgetNode *node)
296{
297    int i;
298    const char **cur;
299    XtResourceList res;
300    XmuWidgetNode **wn;
301    ViewresData *d = VData(node);
302
303    if (!d->resource_labels) {
304	d->resource_labels =
305	  (const char **) calloc ((unsigned) d->nnew * 3,
306				  (unsigned) sizeof (const char *));
307	if (!d->resource_labels) return FALSE;
308    }
309
310    cur = d->resource_labels;
311    res = node->resources;
312    wn = node->resourcewn;
313    for (i = 0; i < node->nresources; i++, res++, wn++) {
314	if (*wn == node) {		/* should match nnew */
315	    *cur++ = res->resource_name;
316	    *cur++ = res->resource_class;
317	    *cur++ = res->resource_type;
318	}
319    }
320    if (d->nnewconstraints > 0) {
321	const char *s;
322
323	*cur++ = s = "";
324	*cur++ = s;
325	*cur++ = s;
326    }
327    res = node->constraints;
328    wn = node->constraintwn;
329    for (i = 0; i < node->nconstraints; i++, res++, wn++) {
330	if (*wn == node) {		/* should match nnew */
331	    *cur++ = res->resource_name;
332	    *cur++ = res->resource_class;
333	    *cur++ = res->resource_type;
334	}
335    }
336    return TRUE;
337}
338
339
340static ViewresData *
341create_viewres_data (XmuWidgetNode *node)
342{
343    ViewresData *d =
344      (ViewresData *) malloc ((unsigned) sizeof(ViewresData));
345
346    if (d) {
347	d->resource_labels = NULL;
348	d->nnewresources = XmuWnCountOwnedResources (node, node, False);
349	d->nnewconstraints = XmuWnCountOwnedResources (node, node, True);
350	d->nnew = (d->nnewresources + (d->nnewconstraints
351				       ? d->nnewconstraints + 1 : 0));
352	d->instance = (Widget) NULL;
353	d->resource_lw = (Widget) NULL;
354	d->selection_index = -1;
355    }
356    return d;
357}
358
359static int
360copydown (int start)
361{
362    XmuWidgetNode **src = &selected_list.elements[start];
363    XmuWidgetNode **dst = src;
364    int cur;
365
366    for (cur = start; start < selected_list.n_elements; start++, src++) {
367	if (*src) {
368	    VData((*src))->selection_index = cur++;
369	    *dst++ = *src;
370	}
371    }
372    return (start - cur);
373}
374
375
376static void
377add_to_selected_list (XmuWidgetNode *node, Boolean updatewidget)
378{
379    ViewresData *d = VData(node);
380    if (!d->instance || d->selection_index >= 0) return;
381
382    if (selected_list.n_elements >= selected_list.max_elements) {
383	initialize_widgetnode_list (&selected_list.elements,
384				    &selected_list.max_elements,
385				    (selected_list.max_elements * 3) / 2);
386    }
387    INSERT_NODE (node, selected_list.n_elements);
388    selected_list.n_elements++;
389
390    if (updatewidget) XtSetValues (d->instance, true_args, ONE);
391}
392
393static Boolean
394remove_from_selected_list (XmuWidgetNode *node, Boolean updatewidget)
395{
396    int i, skips;
397    ViewresData *d = VData(node);
398
399    if ((i = d->selection_index) < 0) return FALSE;
400
401    REMOVE_NODE (node);
402
403    /* copy down */
404    if (selected_list.n_elements > 1) {
405	skips = copydown (i);
406    } else {
407	skips = 1;
408    }
409    selected_list.n_elements -= skips;
410
411    if (updatewidget) XtSetValues (d->instance, false_args, ONE);
412    return TRUE;
413}
414
415static void
416remove_nodes_from_selected_list (int start, int count, Boolean updatewidget)
417{
418    int i;
419
420    for (i = 0; i < count; i++) {
421	XmuWidgetNode *p = selected_list.elements[start+i];
422	ViewresData *d = VData(p);
423	REMOVE_NODE (p);
424	if (updatewidget) XtSetValues (d->instance, false_args, ONE);
425    }
426    selected_list.n_elements -= copydown (start);
427}
428
429static void
430add_subtree_to_selected_list (XmuWidgetNode *node, Boolean updatewidget)
431{
432    if (!node) return;
433
434    add_to_selected_list (node, updatewidget);
435    for (node = node->children; node; node = node->siblings) {
436	add_subtree_to_selected_list (node, updatewidget);
437    }
438}
439
440
441/* ARGSUSED */
442static void
443variable_labeltype_callback (Widget gw,
444    XtPointer closure,		/* TRUE or FALSE */
445    XtPointer data)
446{
447    set_labeltype_menu ((Boolean) (long) closure, True);
448}
449
450/* ARGSUSED */
451static void
452gravity_callback (Widget gw,
453    XtPointer closure,		/* TRUE or FALSE */
454    XtPointer data)
455{
456    set_orientation_menu ((XtGravity) (long) closure, True);
457}
458
459
460static Boolean
461create_resource_lw (XmuWidgetNode *node)
462{
463    Arg args[4];
464    Cardinal n;
465    ViewresData *d = VData(node);
466
467    if (d->nnew == 0) return FALSE;
468
469    if (!d->resource_labels &&
470	!set_resource_labels (node)) return FALSE;
471
472    n = 0;
473    XtSetArg (args[n], XtNnumberStrings, 3 * d->nnew); n++;
474    XtSetArg (args[n], XtNlist, d->resource_labels); n++;
475    XtSetArg (args[n], XtNdefaultColumns, 3); n++;
476    XtSetArg (args[n], XtNforceColumns, TRUE); n++;
477    d->resource_lw = XtCreateManagedWidget (node->label, listWidgetClass,
478					    XtParent(d->instance),
479					    args, n);
480    XtRealizeWidget (d->resource_lw);
481    return TRUE;
482}
483
484static void
485update_selection_items (void)
486{
487    int i;
488    static Arg args[1] = {{ XtNsensitive, (XtArgVal) FALSE }};
489    Boolean show = FALSE, hide = FALSE, ancestors = FALSE;
490    Boolean descendants = FALSE;
491
492    for (i = 0; i < selected_list.n_elements; i++) {
493	XmuWidgetNode *node = selected_list.elements[i];
494	ViewresData *d = VData(node);
495
496	/*
497	 * If node has any new resources then may be shown (if not
498	 * already being shown).  If node has widget and is managed,
499	 * then may be hidden.
500	 */
501	if (d->nnew > 0) {
502	    if (IsShowing(node)) {
503		hide = TRUE;
504	    } else {
505		show = TRUE;
506	    }
507	}
508	if (node != topnode) ancestors = TRUE;
509	if (node->children) descendants = TRUE;
510    }
511
512    args[0].value = (XtArgVal) show;
513    XtSetValues (view_widgets[VIEW_SHOW_RESOURCES], args, ONE);
514    args[0].value = (XtArgVal) hide;
515    XtSetValues (view_widgets[VIEW_HIDE_RESOURCES], args, ONE);
516    args[0].value = (XtArgVal) (selected_list.n_elements > 0 ? TRUE : FALSE);
517    XtSetValues (select_widgets[SELECT_NOTHING], args, ONE);
518    args[0].value = (XtArgVal) ancestors;
519    XtSetValues (select_widgets[SELECT_PARENT], args, ONE);
520    XtSetValues (select_widgets[SELECT_ANCESTORS], args, ONE);
521    args[0].value = (XtArgVal) descendants;
522    XtSetValues (select_widgets[SELECT_CHILDREN], args, ONE);
523    XtSetValues (select_widgets[SELECT_DESCENDANTS], args, ONE);
524    args[0].value = (XtArgVal) ((Boolean) (NumberShowing > 0));
525    XtSetValues (select_widgets[SELECT_SHOWN_RESOURCES], args, ONE);
526}
527
528
529static void
530do_resources (XmuWidgetNode *node, Boolean op, Boolean updatewidget)
531{
532    ViewresData *d = VData(node);
533    if (op == BOOL_TOGGLE) op = (IsShowing(node) ? BOOL_OFF : BOOL_ON);
534
535    if (op == BOOL_ON) {
536	if (d->resource_lw) {		/* if already created */
537	    if (!XtIsManaged(d->resource_lw)) {
538		NumberShowing++;
539		XtManageChild (d->resource_lw);
540	    }				/* else ignore it */
541	} else if (create_resource_lw (node))	/* create it */
542	  NumberShowing++;
543    } else if (d->resource_lw) {		/* if already created */
544	if (XtIsManaged (d->resource_lw)) {
545	    NumberShowing--;
546	    XtUnmanageChild (d->resource_lw);
547	    XawListUnhighlight (d->resource_lw);
548	    if (updatewidget) remove_from_selected_list (node, TRUE);
549	}				/* else ignore it */
550    }
551}
552
553
554
555/* ARGSUSED */
556static void
557show_resources_callback (
558    Widget gw,				/* menu or toggle button */
559    XtPointer closure,			/* BOOL_OFF, BOOL_ON, BOOL_TOGGLE */
560    XtPointer data)			/* undefined */
561{
562    int op = (long) closure;
563    XmuWidgetNode *node = widget_to_node (gw);
564
565    if (node) {
566	XUnmapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
567	do_resources (node, op, TRUE);
568    } else if (selected_list.n_elements <= 0) {
569	return;
570    } else {
571	int i;
572
573	XUnmapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
574	for (i = 0; i < selected_list.n_elements; i++) {
575	    do_resources (selected_list.elements[i], op, FALSE);
576	}
577    }
578    XawTreeForceLayout (treeWidget);
579    XMapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
580    update_selection_items ();
581}
582
583
584/* ARGSUSED */
585static void
586select_callback (
587    Widget gw,				/* entry widget */
588    XtPointer closure,			/* TRUE or FALSE */
589    XtPointer data)			/* undefined */
590{
591    int i;
592    int nselected = selected_list.n_elements;
593    XmuWidgetNode *node;
594
595    switch ((long) closure) {
596      case SELECT_NOTHING:		/* clear selection_list */
597	remove_nodes_from_selected_list (0, nselected, True);
598	break;
599
600      case SELECT_ALL:			/* put everything on selection_list */
601	add_subtree_to_selected_list (topnode, TRUE);
602	break;
603
604      case SELECT_INVERT:		/* toggle selection state */
605	for (i = 0, node = widget_list; i < nwidgets; i++, node++) {
606	    ViewresData *d = VData(node);
607	    if (d->selection_index < 0) add_to_selected_list (node, TRUE);
608	}
609	remove_nodes_from_selected_list (0, nselected, True);
610	break;
611
612
613      case SELECT_PARENT:		/* choose immediate parent */
614	node = widget_to_node (gw);
615	if (node) {
616	    if (node->superclass)
617	      add_to_selected_list (node->superclass, TRUE);
618	} else {
619	    for (i = 0; i < nselected; i++) {
620		XmuWidgetNode *node = selected_list.elements[i];
621		if (node->superclass)
622		  add_to_selected_list (node->superclass, TRUE);
623	    }
624	}
625	break;
626
627      case SELECT_ANCESTORS:		/* chain up adding to selection_list */
628	node = widget_to_node (gw);
629	if (node) {
630	    do {
631		add_to_selected_list (node, TRUE);
632	    } while ((node = node->superclass) != NULL);
633	} else {
634	    for (i = 0; i < nselected; i++) {
635		XmuWidgetNode *parent = selected_list.elements[i];
636
637		/*
638		 * chain up the tree, but stop if we get to nodes that
639		 * are already in the selected list.
640		 */
641		while ((parent = parent->superclass) != NULL) {
642		    if (VData(parent)->selection_index >= 0) break;
643		    add_to_selected_list (parent, TRUE);
644		}
645	    }
646	}
647	break;
648
649      case SELECT_CHILDREN:		/* all direct sub nodes */
650	node = widget_to_node (gw);
651	if (node) {
652	    add_to_selected_list (node, TRUE);
653	    for (node = node->children; node; node = node->siblings) {
654		add_to_selected_list (node, TRUE);
655	    }
656	} else {
657	    for (i = 0; i < nselected; i++) {
658		XmuWidgetNode *node = selected_list.elements[i];
659
660		add_to_selected_list (node, TRUE);
661		for (node = node->children; node; node = node->siblings) {
662		    add_to_selected_list (node, TRUE);
663		}
664	    }
665	}
666	break;
667
668      case SELECT_DESCENDANTS:		/* all sub nodes */
669	node = widget_to_node (gw);
670	if (node) {
671	    add_subtree_to_selected_list (node, TRUE);
672	} else {
673	    for (i = 0; i < nselected; i++) {
674		XmuWidgetNode *parent = selected_list.elements[i];
675
676		add_subtree_to_selected_list (parent, TRUE);
677	    }
678	}
679	break;
680
681      case SELECT_HAS_RESOURCES:	/* put all w/ rescnt > 0 on sel_list */
682	for (i = 0, node = widget_list; i < nwidgets; i++, node++) {
683	    if (VData(node)->nnew > 0)
684	      add_to_selected_list (node, TRUE);
685	}
686	break;
687
688      case SELECT_SHOWN_RESOURCES:
689	for (i = 0, node = widget_list; i < nwidgets; i++, node++) {
690	    if (IsShowing(node)) add_to_selected_list (node, TRUE);
691	}
692	break;
693
694      default:				/* error!!! */
695	XBell (XtDisplay(gw), 0);
696	return;
697    }
698
699    update_selection_items ();
700}
701
702/* ARGSUSED */
703static void
704toggle_callback (Widget gw,
705    XtPointer closure,		/* XmuWidgetNode for this widget */
706    XtPointer data)		/* on or off */
707{
708    XmuWidgetNode *node = (XmuWidgetNode *) closure;
709    Boolean selected = (Boolean) (long) data;
710
711    if (selected) {
712	add_to_selected_list (node, FALSE);
713    } else {
714	(void) remove_from_selected_list (node, FALSE);
715    }
716
717    update_selection_items ();
718}
719
720
721/*
722 * panner/porthole controls - called when the other changes
723 */
724/* ARGSUSED */
725static void
726panner_callback (
727    Widget gw,				/* panner widget */
728    XtPointer closure,			/* porthole widget */
729    XtPointer data)			/* report */
730{
731    XawPannerReport *rep = (XawPannerReport *) data;
732    Arg args[2];
733
734    if (!treeWidget) return;
735
736    XtSetArg (args[0], XtNx, -rep->slider_x);
737    XtSetArg (args[1], XtNy, -rep->slider_y);
738    XtSetValues (treeWidget, args, TWO);	/* just assume... */
739}
740
741/* ARGSUSED */
742static void
743porthole_callback (
744    Widget gw,				/* porthole widget */
745    XtPointer closure,			/* panner widget */
746    XtPointer data)			/* report */
747{
748    Widget panner = (Widget) closure;
749    XawPannerReport *rep = (XawPannerReport *) data;
750    Arg args[6];
751    Cardinal n = TWO;
752
753    XtSetArg (args[0], XtNsliderX, rep->slider_x);
754    XtSetArg (args[1], XtNsliderY, rep->slider_y);
755    if (rep->changed != (XawPRSliderX | XawPRSliderY)) {
756	XtSetArg (args[2], XtNsliderWidth, rep->slider_width);
757	XtSetArg (args[3], XtNsliderHeight, rep->slider_height);
758	XtSetArg (args[4], XtNcanvasWidth, rep->canvas_width);
759	XtSetArg (args[5], XtNcanvasHeight, rep->canvas_height);
760	n = SIX;
761    }
762    XtSetValues (panner, args, n);
763}
764
765
766
767static void
768build_tree (XmuWidgetNode *node, Widget tree, Widget super)
769{
770    ViewresData *d = VData (node);
771    Widget box, w;			/* widget for this Class */
772    XmuWidgetNode *child;			/* iterator over children */
773    Arg args[3];			/* need to set super node */
774    Cardinal n;				/* count of args */
775    static XtCallbackRec callback_rec[2] = {{ toggle_callback, NULL },
776					     { NULL, NULL }};
777
778
779    n = 0;
780    XtSetArg (args[n], XtNtreeParent, super); n++;
781    box = XtCreateManagedWidget (node->label, boxWidgetClass, tree, args, n);
782
783    n = 0;
784    XtSetArg (args[n], XtNlabel, (options.show_variable ?
785				  node->label : XmuWnClassname(node))); n++;
786    XtSetArg (args[n], XtNcallback, callback_rec); n++;
787
788    callback_rec[0].closure = (XtPointer) node;
789    w = XtCreateManagedWidget (node->label, toggleWidgetClass, box, args, n);
790    d->instance = w;
791
792    /*
793     * recursively build the rest of the tree
794     */
795    for (child = node->children; child; child = child->siblings) {
796	build_tree (child, tree, box);
797    }
798}
799
800
801static void
802set_node_labels (XmuWidgetNode *node, int depth)
803{
804    Arg args[1];
805    XmuWidgetNode *child;
806    ViewresData *d = VData(node);
807
808    if (!node) return;
809    XtSetArg (args[0], XtNlabel, (options.show_variable ?
810				  node->label : XmuWnClassname(node)));
811    XtSetValues (d->instance, args, ONE);
812
813    for (child = node->children; child; child = child->siblings) {
814	set_node_labels (child, depth + 1);
815    }
816}
817
818
819static void
820oneof_sensitive (Boolean choosea, Widget a, Widget b)
821{
822    static Arg args[1] = { { XtNsensitive, (XtArgVal) NULL } };
823
824    args[0].value = (XtArgVal) TRUE;
825    XtSetValues (choosea ? a : b, args, ONE);
826    args[0].value = (XtArgVal) FALSE;
827    XtSetValues (choosea ? b : a, args, ONE);
828}
829
830static void
831set_labeltype_menu (Boolean isvar, Boolean doall)
832{
833    options.show_variable = isvar;
834    oneof_sensitive (isvar, view_widgets[VIEW_CLASSES],
835		     view_widgets[VIEW_VARIABLES]);
836
837    if (doall) {
838	XUnmapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
839	set_node_labels (topnode, 0);
840	XawTreeForceLayout (treeWidget);
841	XMapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
842    }
843}
844
845static void
846set_orientation_menu (XtGravity grav, Boolean dosetvalues)
847{
848#define CHOOSE(val) (sensitiveargs + (grav != (val)))
849    XtSetValues (view_widgets[VIEW_HORIZONTAL], CHOOSE(WestGravity), ONE);
850    XtSetValues (view_widgets[VIEW_VERTICAL], CHOOSE(NorthGravity), ONE);
851#undef CHOOSE
852
853    if (dosetvalues) {
854	Arg args[1];
855
856	XtSetArg (args[0], XtNgravity, grav);
857	XUnmapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
858 	XtSetValues (treeWidget, args, ONE);
859	XMapWindow (XtDisplay(treeWidget), XtWindow(treeWidget));
860    }
861}
862
863
864/*****************************************************************************
865 *                                                                           *
866 *		     viewres - visual class browser for Xt                   *
867 *                                                                           *
868 *****************************************************************************/
869
870int
871main (int argc, char *argv[])
872{
873    Widget toplevel, pane, box, dummy, porthole, panner, form;
874    XmuWidgetNode *rootNode; /* always the root of the resource hierarchy */
875    XtAppContext app_con;
876    Arg args[6];
877    Dimension canvasWidth, canvasHeight, sliderWidth, sliderHeight;
878    static XtCallbackRec callback_rec[2] = {{ NULL, NULL }, { NULL, NULL }};
879    XtGravity grav;
880    int i;
881
882    ProgramName = argv[0];
883
884    XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
885
886    toplevel = XtAppInitialize (&app_con, "Viewres",
887				Options, XtNumber (Options),
888				&argc, argv, (char **) fallback_resources,
889				(ArgList) NULL, ZERO);
890    if (argc != 1) usage ();
891
892    initialize_widgetnode_list (&selected_list.elements,
893				&selected_list.max_elements, 10);
894
895    XtGetApplicationResources (toplevel, (XtPointer) &options,
896			       Resources, XtNumber(Resources), NULL, ZERO);
897    XmuWnInitializeNodes (widget_list, nwidgets);
898
899    topnode = XmuWnNameToNode (widget_list, nwidgets, options.top_object);
900    if (!topnode) {
901	fprintf(stderr, "%s: no widget with name \"%s\" found.\n",
902		ProgramName, options.top_object);
903	exit(1);
904    }
905
906    XtAppAddActions (app_con, viewres_actions, XtNumber (viewres_actions));
907    XtOverrideTranslations
908	(toplevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()"));
909
910    /*
911     * create dummy widgets to initialize resources
912     */
913    XtSetArg (args[0], XtNwidth, 1);
914    XtSetArg (args[1], XtNheight, 1);
915    dummy = XtCreateWidget ("dummy", widgetClass, toplevel, args, TWO);
916    rootNode = XmuWnNameToNode(widget_list, nwidgets, "Object");
917    for (i = 0; i < nwidgets; i++) {
918	XmuWidgetNode *node = &widget_list[i];
919	XmuWnFetchResources (node, dummy, rootNode);
920	node->data = (XtPointer) create_viewres_data (node);
921    }
922    XtDestroyWidget (dummy);
923
924    pane = XtCreateManagedWidget ("pane", panedWidgetClass, toplevel,
925				  (ArgList) NULL, ZERO);
926
927    box = XtCreateManagedWidget ("buttonbox", boxWidgetClass, pane,
928				 (ArgList) NULL, ZERO);
929    (void) XtCreateManagedWidget ("quit", commandWidgetClass, box,
930				  (ArgList) NULL, ZERO);
931
932    /*
933     * Format menu
934     */
935    XtSetArg (args[0], XtNmenuName, "viewMenu");
936    viewButton = XtCreateManagedWidget ("view", menuButtonWidgetClass, box,
937					args, ONE);
938    viewMenu = XtCreatePopupShell ("viewMenu", simpleMenuWidgetClass,
939				   viewButton, (ArgList) NULL, ZERO);
940    XtSetArg (args[0], XtNcallback, callback_rec);
941
942#define MAKE_VIEW(n,v,name) \
943    callback_rec[0].closure = (XtPointer) v; \
944    view_widgets[n] = XtCreateManagedWidget (name, smeBSBObjectClass, \
945					     viewMenu, args, ONE)
946    callback_rec[0].callback = (XtCallbackProc) gravity_callback;
947    MAKE_VIEW (VIEW_HORIZONTAL, WestGravity, "layoutHorizontal");
948    MAKE_VIEW (VIEW_VERTICAL, NorthGravity, "layoutVertical");
949
950    (void) XtCreateManagedWidget ("line1", smeLineObjectClass, viewMenu,
951				  (ArgList) NULL, ZERO);
952
953    callback_rec[0].callback = (XtCallbackProc) variable_labeltype_callback;
954    MAKE_VIEW (VIEW_VARIABLES, TRUE, "namesVariable");
955    MAKE_VIEW (VIEW_CLASSES, FALSE, "namesClass");
956
957    (void) XtCreateManagedWidget ("line2", smeLineObjectClass, viewMenu,
958				  (ArgList) NULL, ZERO);
959
960    callback_rec[0].callback = (XtCallbackProc) show_resources_callback;
961    MAKE_VIEW (VIEW_SHOW_RESOURCES, BOOL_ON, "viewResources");
962    MAKE_VIEW (VIEW_HIDE_RESOURCES, BOOL_OFF, "viewNoResources");
963#undef MAKE_VIEW
964
965    /*
966     * Select menu
967     */
968    XtSetArg (args[0], XtNmenuName, "selectMenu");
969    selectButton = XtCreateManagedWidget ("select", menuButtonWidgetClass, box,
970					  args, ONE);
971    selectMenu = XtCreatePopupShell ("selectMenu", simpleMenuWidgetClass,
972				     selectButton, (ArgList) NULL, ZERO);
973    XtSetArg (args[0], XtNcallback, callback_rec);
974    callback_rec[0].callback = (XtCallbackProc) select_callback;
975#define MAKE_SELECT(n,name) \
976    callback_rec[0].closure = (XtPointer) n; \
977    select_widgets[n] = XtCreateManagedWidget (name, smeBSBObjectClass, \
978					       selectMenu, args, ONE)
979    MAKE_SELECT (SELECT_NOTHING, "unselect");
980    MAKE_SELECT (SELECT_ALL, "selectAll");
981    MAKE_SELECT (SELECT_INVERT, "selectInvert");
982    (void) XtCreateManagedWidget ("line1", smeLineObjectClass, selectMenu,
983				  (ArgList) NULL, ZERO);
984    MAKE_SELECT (SELECT_PARENT, "selectParent");
985    MAKE_SELECT (SELECT_ANCESTORS, "selectAncestors");
986    MAKE_SELECT (SELECT_CHILDREN, "selectChildren");
987    MAKE_SELECT (SELECT_DESCENDANTS, "selectDescendants");
988    (void) XtCreateManagedWidget ("line2", smeLineObjectClass, selectMenu,
989				  (ArgList) NULL, ZERO);
990    MAKE_SELECT (SELECT_HAS_RESOURCES, "selectHasResources");
991    MAKE_SELECT (SELECT_SHOWN_RESOURCES, "selectShownResources");
992#undef MAKE_SELECT
993
994    form = XtCreateManagedWidget ("treeform", formWidgetClass, pane,
995				  (ArgList) NULL, ZERO);
996    /*
997     * create the panner and the porthole and then connect them with the
998     * callbacks (passing the other widget each callback)
999     */
1000    XtSetArg (args[0], XtNbackgroundPixmap, None);  /* faster updates */
1001    porthole = XtCreateManagedWidget ("porthole", portholeWidgetClass, form,
1002				      args, ONE);
1003    panner = XtCreateManagedWidget ("panner", pannerWidgetClass, form,
1004				    (ArgList) NULL, ZERO);
1005
1006    XtSetArg (args[0], XtNreportCallback, callback_rec);
1007    callback_rec[0].callback = (XtCallbackProc) panner_callback;
1008    callback_rec[0].closure = (XtPointer) porthole;
1009    XtSetValues (panner, args, ONE);
1010
1011    callback_rec[0].callback = (XtCallbackProc) porthole_callback;
1012    callback_rec[0].closure = (XtPointer) panner;
1013    XtSetValues (porthole, args, ONE);
1014
1015    /*
1016     * now that the panner and porthole are set up, insert the tree and
1017     * fix up the menu, fill in the nodes
1018     */
1019    treeWidget = XtCreateManagedWidget ("tree", treeWidgetClass,
1020					porthole, (ArgList) NULL, ZERO);
1021
1022    set_labeltype_menu (options.show_variable, FALSE);
1023    XtSetArg (args[0], XtNgravity, &grav);
1024    XtGetValues (treeWidget, args, ONE);
1025    set_orientation_menu (grav, FALSE);
1026    update_selection_items ();
1027    build_tree (topnode, treeWidget, (Widget) NULL);
1028
1029    /*
1030     * Realize the tree, but do not map it (we set mappedWhenManaged to
1031     * false up above).  Get the initial size of the tree so that we can
1032     * size the panner appropriately.
1033     */
1034    XtRealizeWidget (toplevel);
1035
1036    wm_delete_window = XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW",
1037				   False);
1038    (void) XSetWMProtocols (XtDisplay(toplevel), XtWindow(toplevel),
1039                            &wm_delete_window, 1);
1040
1041    XtSetArg (args[0], XtNwidth, &canvasWidth);
1042    XtSetArg (args[1], XtNheight, &canvasHeight);
1043    XtGetValues (treeWidget, args, TWO);
1044
1045    XtSetArg (args[0], XtNwidth, &sliderWidth);
1046    XtSetArg (args[1], XtNheight, &sliderHeight);
1047    XtGetValues (porthole, args, TWO);
1048
1049    XtSetArg (args[0], XtNcanvasWidth, canvasWidth);
1050    XtSetArg (args[1], XtNcanvasHeight, canvasHeight);
1051    XtSetArg (args[2], XtNsliderWidth, sliderWidth);
1052    XtSetArg (args[3], XtNsliderHeight, sliderHeight);
1053    XtSetValues (panner, args, FOUR);
1054
1055    XRaiseWindow (XtDisplay(panner), XtWindow(panner));
1056    XtAppMainLoop (app_con);
1057
1058    return 0;
1059}
1060
1061
1062
1063/*****************************************************************************
1064 *                                                                           *
1065 *		   viewres translation table action routines                 *
1066 *                                                                           *
1067 *****************************************************************************/
1068
1069/* ARGSUSED */
1070static void
1071ActionQuit (Widget w, XEvent *event,
1072    String *params, Cardinal *num_params)
1073{
1074    exit (0);
1075}
1076
1077/* ARGSUSED */
1078static void
1079ActionSetLableType (Widget w, XEvent *event,
1080    String *params, Cardinal *num_params)
1081{
1082    const char *cmd;
1083    Boolean oldvar = options.show_variable, newvar;
1084
1085    switch (*num_params) {
1086      case 0:
1087	cmd = "toggle";
1088	break;
1089      case 1:
1090	cmd = params[0];
1091	break;
1092      default:
1093	XBell (XtDisplay(w), 0);
1094	return;
1095    }
1096
1097    if (XmuCompareISOLatin1 (cmd, "toggle") == 0) {
1098	newvar = !oldvar;
1099    } else if (XmuCompareISOLatin1 (cmd, "variable") == 0) {
1100	newvar = TRUE;
1101    } else if (XmuCompareISOLatin1 (cmd, "class") == 0) {
1102	newvar = FALSE;
1103    } else {
1104	XBell (XtDisplay(w), 0);
1105	return;
1106    }
1107
1108    if (newvar != oldvar) set_labeltype_menu (newvar, TRUE);
1109    return;
1110}
1111
1112/* ARGSUSED */
1113static void
1114ActionSetOrientation (Widget w, XEvent *event,
1115    String *params, Cardinal *num_params)
1116{
1117    XtGravity newgrav = ForgetGravity;
1118
1119    if (*num_params < 1) {
1120	Arg arg;
1121	XtGravity oldgrav = ForgetGravity;
1122
1123	XtSetArg (arg, XtNgravity, &oldgrav);
1124	XtGetValues (treeWidget, &arg, ONE);
1125	switch (oldgrav) {
1126	  case WestGravity:  newgrav = NorthGravity; break;
1127	  case NorthGravity:  newgrav = WestGravity; break;
1128	  case EastGravity:  newgrav = SouthGravity; break;
1129	  case SouthGravity:  newgrav = EastGravity; break;
1130	  default:
1131	    return;
1132	}
1133    } else {
1134	XrmValue fromval, toval;
1135
1136	fromval.size = sizeof (String);
1137	fromval.addr = (XPointer) params[0];
1138	toval.size = sizeof (XtGravity);
1139	toval.addr = (XPointer) &newgrav;
1140	XtConvertAndStore (treeWidget, XtRString, &fromval,
1141			   XtRGravity, &toval);
1142    }
1143
1144    switch (newgrav) {
1145      case WestGravity: case NorthGravity: case EastGravity: case SouthGravity:
1146	break;
1147      default:
1148	XBell (XtDisplay(w), 0);
1149	return;
1150    }
1151
1152    set_orientation_menu (newgrav, TRUE);
1153    return;
1154}
1155
1156
1157static void
1158do_single_arg (Widget w, String *params, Cardinal nparams,
1159    struct _nametable table[] , int nentries, XtCallbackProc proc)
1160{
1161    int obj;
1162    int i;
1163
1164    if (nparams != 1) {
1165	XBell (XtDisplay(w), 0);
1166	return;
1167    }
1168
1169    for (i = 0; i < nentries; i++) {
1170	if (XmuCompareISOLatin1 (params[0], table[i].name) == 0) {
1171	    obj = table[i].value;
1172	    goto found;
1173	}
1174    }
1175    XBell (XtDisplay(w), 0);
1176    return;
1177
1178  found:
1179    /*
1180     * use any old widget
1181     */
1182    (*proc) (w, (XtPointer) (long) obj, (XtPointer) NULL);
1183}
1184
1185
1186/* ARGSUSED */
1187static void
1188ActionSelect (Widget w, XEvent *event,
1189    String *params, Cardinal *num_params)
1190{
1191    do_single_arg (w, params, *num_params, select_nametable,
1192		   (int) XtNumber(select_nametable), select_callback);
1193}
1194
1195
1196/* ARGSUSED */
1197static void ActionResources (Widget w, XEvent *event,
1198    String *params, Cardinal *num_params)
1199{
1200    if (*num_params == 0) {
1201	show_resources_callback (w, (XtPointer) BOOL_TOGGLE, (XtPointer) NULL);
1202    } else {
1203	do_single_arg (w, params, *num_params, boolean_nametable,
1204		       (int) XtNumber(boolean_nametable),
1205		       show_resources_callback);
1206    }
1207}
1208
1209