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