1/*
2
3Copyright 1987, 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26/*****************************************************************
27
28(C) COPYRIGHT International Business Machines Corp. 1992,1997
29    All Rights Reserved
30
31Permission is hereby granted, free of charge, to any person obtaining a copy
32of this software and associated documentation files (the "Software"), to deal
33in the Software without restriction, including without limitation the rights
34to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35copies of the Software.
36
37The above copyright notice and this permission notice shall be included in
38all copies or substantial portions of the Software.
39
40THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
43THE IBM CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
44BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
45WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
46IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47
48Except as contained in this notice, the name of the IBM Corporation shall
49not be used in advertising or otherwise to promote the sale, use or other
50dealings in this Software without prior written authorization from the IBM
51Corporation.
52
53******************************************************************/
54
55#ifdef HAVE_CONFIG_H
56#include <config.h>
57#endif
58#include "Intrinsic.h"
59#include "IntrinsicI.h"
60#include "Core.h"
61#include "CoreP.h"
62#include "ShellP.h"
63#include "StringDefs.h"
64#include "ResConfigP.h"
65#include <X11/Xatom.h>
66#include <stdio.h>
67#include <stdlib.h>
68
69#define MAX_BUFFER 512
70
71static void _search_child(Widget, char *, char *, char *, char *, char, char *);
72static void _set_and_search(Widget, char *, char *, char *, char *, char,
73                            char *);
74static int _locate_children(Widget, Widget **);
75
76/*
77 * NAME: _set_resource_values
78 *
79 * FUNCTION:
80 *      This function sets the value on the widget.  It must first determine
81 *      if the last part is a valid resource for that widget.  (eg.
82 *      labelString is a valid resource for label but not for bulletin board)
83 *      It must also add the resource to the application's resource database
84 *      and then query it out using specific resource strings that it builds
85 *      from the widget information.  This ensures that a customizing tool
86 *      on-the-fly paradigm is followed:  an application that is
87 *      instantaneously updated should look the same as one that is restarted
88 *      and uses the .Xdefaults file.
89 *
90 * PARAMETERS:
91 *      w               the widget to match
92 *      resource        the resource string to be matched
93 *      value           the value to be set
94 *      last_part       the last resource part (e.g. *background)
95 *
96 * RETURN VALUES: void
97 *
98 * ERRORS: none
99 */
100static void
101_set_resource_values(Widget w, char *resource, char *value, char *last_part)
102{
103    XrmDatabase db = NULL;
104    char *resource_name = NULL;
105    char *resource_class = NULL;
106    char *return_type;
107    XrmValue return_value;
108    char *resource_value;
109    Widget cur = w;
110    char *temp;
111    XtResourceList resources_return = NULL;
112    Cardinal num_resources_return = 0;
113    Cardinal res_index;
114    Boolean found_resource = False;
115    Display *dpy;
116    XrmDatabase tmp_db;
117
118    if (last_part == NULL)
119        return;
120
121    if (!XtIsWidget(w)) {
122        if (w == 0 || w->core.parent == 0)
123            return;
124        dpy = XtDisplay(w->core.parent);
125    }
126    else {
127        dpy = XtDisplay(w);
128    }
129    tmp_db = XtDatabase(dpy);
130
131    /*
132     * get a list of all the valid resources for this widget
133     */
134    XtGetResourceList(w->core.widget_class,
135                      &resources_return, &num_resources_return);
136
137    /*
138     * try to match the last_part of the resource string with
139     * a resource in this resource list
140     */
141    for (res_index = 0; res_index < num_resources_return; res_index++) {
142        if ((strcmp(last_part,
143                    resources_return[res_index].resource_name) == 0) ||
144            (strcmp(last_part,
145                    resources_return[res_index].resource_class) == 0)) {
146            found_resource = True;
147            break;
148        }
149    }
150
151    /*
152     * if resource is not a valid resource for this widget
153     * or the resource name or class are NULL
154     * then exit this function
155     */
156    if (!found_resource
157        || !resources_return[res_index].resource_name
158        || !resources_return[res_index].resource_class) {
159        XtFree((char *) resources_return);
160        return;
161    }
162
163    /*
164     * build the full resource name and class specifications so
165     * that you can query the resource database
166     *      eg: .app.button1.foreground
167     *          .App.XmPushButton.Foreground
168     */
169    while (cur != NULL) {
170        /*
171         * create resource name string
172         */
173        if (resource_name) {
174            XtAsprintf(&temp, ".%s%s", cur->core.name, resource_name);
175            XtFree(resource_name);
176        }
177        else if (!XtIsWidget(cur) || !cur->core.name) {
178            cur = XtParent(cur);
179            continue;
180        }
181        else {
182            XtAsprintf(&temp, ".%s", cur->core.name);
183        }
184        resource_name = temp;
185
186        /*
187         * create resource class string
188         */
189        if ((XtIsTopLevelShell(cur)) && (XtParent(cur) == NULL)) {
190            ApplicationShellWidget top = (ApplicationShellWidget) (cur);
191
192            if (resource_class) {
193                XtAsprintf(&temp, ".%s%s",
194                           top->application.class, resource_class);
195            }
196            else {
197                XtAsprintf(&temp, ".%s", top->application.class);
198            }
199        }
200        else {
201            if (resource_class) {
202                XtAsprintf(&temp, ".%s%s",
203                           cur->core.widget_class->core_class.class_name,
204                           resource_class);
205            }
206            else {
207                XtAsprintf(&temp, ".%s",
208                           cur->core.widget_class->core_class.class_name);
209            }
210        }
211
212        XtFree(resource_class);
213        resource_class = temp;
214
215        cur = XtParent(cur);
216    }
217
218    /*
219     * add the resource name to the end of the resource name string
220     */
221    XtAsprintf(&temp, "%s.%s", resource_name,
222               resources_return[res_index].resource_name);
223
224    XtFree(resource_name);
225    resource_name = temp;
226
227    /*
228     * add the resource class to the end of the resource class string
229     */
230    XtAsprintf(&temp, "%s.%s", resource_class,
231               resources_return[res_index].resource_class);
232
233    XtFree(resource_class);
234    resource_class = temp;
235
236#ifdef DEBUG
237    fprintf(stderr, "resource_name = %s\n", resource_name);
238    fprintf(stderr, "resource_class = %s\n", resource_class);
239#endif
240
241    /*
242     * put the resource and its value in a resource database and
243     * then query it back out again using the specific name and
244     * class resource strings that were built above.  This is
245     * necessary to maintain a precedence similar to the .Xdefaults
246     * file
247     */
248    XrmPutStringResource(&db, resource, value);
249    XrmMergeDatabases(db, &tmp_db);
250    XrmGetResource(tmp_db, resource_name, resource_class,
251                   &return_type, &return_value);
252    if (return_type)
253        resource_value = XtNewString(return_value.addr);
254    else
255        resource_value = XtNewString(value);
256
257#ifdef DEBUG
258    fprintf(stderr,
259            "Apply:\n\twidget = %s\n\tlast_part = %s\n\tvalue = %s\n",
260            (w->core.name == NULL) ? "NULL" : w->core.name,
261            resources_return[res_index].resource_name, resource_value);
262#endif
263    /*
264     * use XtVaSetValues with XtVaTypedArg to convert the value of
265     * type String the the same type as the resource (last_part).
266     * Then set the value.
267     */
268    XtVaSetValues(w,
269                  XtVaTypedArg, resources_return[res_index].resource_name,
270                  XtRString, resource_value, strlen(resource_value) + 1, NULL);
271
272    XtFree((char *) resources_return);
273    XtFree(resource_name);
274    XtFree(resource_class);
275    XtFree(resource_value);
276}
277
278/*
279 * NAME: _apply_values_to_children
280 *
281 * FUNCTION:
282 *      Once the resource string matches the value must be applied to
283 *      all children if applicable. (eg. App*Form.background must apply
284 *      background to all children of the Form widget)
285 *
286 * PARAMETERS:
287 *      w               the widget to match
288 *      remainder       the part of the resource string left over
289 *      resource        the resource string to be matched
290 *      value           the value to be set
291 *      last_token      the last * or . before the final resource part
292 *      last_part       the last resource part (e.g. *background)
293 *
294 * RETURN VALUES: void
295 *
296 * ERRORS: none
297 */
298static void
299_apply_values_to_children(Widget w,
300                          char *remainder,
301                          char *resource,
302                          char *value,
303                          char last_token,
304                          char *last_part)
305{
306    int i;
307    int num_children;
308    Widget *children;
309
310    /*
311     * Recursively search through the children
312     */
313    num_children = _locate_children(w, &children);
314
315    for (i = 0; i < num_children; i++) {
316
317#ifdef DEBUG
318        if (XtIsWidget(children[i]) && XtIsWidget(w))
319            fprintf(stderr, "searching child %s of parent %s\n",
320                    children[i]->core.name, w->core.name);
321        else
322            fprintf(stderr, "searching child (NULL) of parent %s\n",
323                    w->core.name);
324        if (!XtIsWidget(children[i]))
325            fprintf(stderr, "children[%d] is NOT a widget\n", i);
326        if (!XtIsWidget(w))
327            fprintf(stderr, "w is NOT a widget\n");
328#endif
329
330        _set_resource_values(children[i], resource, value, last_part);
331        _apply_values_to_children(children[i], remainder,
332                                  resource, value, last_token, last_part);
333    }
334
335    XtFree((char *) children);
336}
337
338/*
339 * NAME: _search_child
340 *
341 * FUNCTION:
342 *      descends through each child of the tree
343 *
344 * PARAMETERS:
345 *      w               the widget whose children are to be searched
346 *      indx            index into the resource string
347 *      remainder       the remaining part of the resource string
348 *      resource        the resource string to be matched
349 *      value           the value to be applied
350 *      last_token      the last * or . before the final resource part
351 *      last_part       the last resource part (e.g. *background)
352 *
353 * RETURN VALUES: none
354 *
355 * ERRORS: none
356 */
357static void
358_search_child(Widget w,
359              char *indx,
360              char *remainder,
361              char *resource,
362              char *value,
363              char last_token,
364              char *last_part)
365{
366    int i;
367    int num_children;
368    Widget *children;
369
370    /*
371     * Recursively search through the children
372     */
373    num_children = _locate_children(w, &children);
374    for (i = 0; i < num_children; i++) {
375        _set_and_search(children[i], indx, remainder, resource,
376                        value, last_token, last_part);
377    }
378
379    XtFree((char *) children);
380}
381
382/*
383 * NAME: _get_part
384 *
385 * FUNCTION:
386 *      This routine will return the token and following part of the resource
387 *      when given the current index it will update the index accordingly
388 *
389 * PARAMETERS:
390 *      remainder       the part of the resource string left over
391 *      indx            the index into the resource string
392 *      part            the parsed off part of the resource string
393 *
394 * RETURN VALUES:
395 *      char            the token (* or . or ?) preceding the resource part
396 *      indx            the index into the resource string
397 *      part            the parsed off part of the resource string
398 *
399 * ERRORS: none
400 */
401static char
402_get_part(char *remainder _X_UNUSED, char **indx, char **part)
403{
404    char buffer[MAX_BUFFER];
405    char *buf_ptr;
406    char token = **indx;
407    int i = 0;
408
409    /*
410     * copy the remainder part into the buffer
411     */
412    buf_ptr = buffer;
413    (*indx)++;                  /* get rid of the token         */
414    while (**indx && (**indx != '.') && (**indx != '*')) {
415        *buf_ptr++ = *(*indx)++;
416        if (++i >= MAX_BUFFER - 1)
417            break;
418    }
419    *buf_ptr = '\0';
420
421    *part = XtNewString(buffer);        /* return a new string to part  */
422
423    if (strcmp(*indx, "") == 0)
424        *indx = NULL;
425
426    return (token);             /* return the token             */
427}
428
429/*
430 * NAME: _match_resource_to_widget
431 *
432 * FUNCTION:
433 *      This function matches the resource part to the widget name or class
434 *
435 * PARAMETERS:
436 *      w               the widget to match
437 *      part            the parsed off part of the resource string
438 *
439 * RETURN VALUES:
440 *      Boolean         true if a match occurs
441 *
442 * ERRORS: none
443 */
444static Boolean
445_match_resource_to_widget(Widget w, char *part)
446{
447    /*
448     * Match any widget at this level if the ? is used
449     */
450    if (strcmp(part, "?") == 0)
451        return (True);
452
453    /*
454     * if the object is really a widget then its name can be matched
455     * otherwise only use its class.  Note that if you try to reference
456     * a widget name when the object is not a widget, you may get a
457     * core dump from an invalid pointer reference.
458     */
459    if (XtIsWidget(w)) {
460        if ((strcmp(w->core.name, part) == 0) ||
461            (strcmp(w->core.widget_class->core_class.class_name, part) == 0))
462            return (True);
463        else
464            return (False);
465    }
466    else {
467        if ((strcmp(w->core.widget_class->core_class.class_name, part) == 0))
468            return (True);
469        else
470            return (False);
471    }
472}
473
474/*
475 * NAME: _set_and_search
476 *
477 * FUNCTION:
478 *      The algorithm to search the widget tree and apply a resource string
479 *
480 * PARAMETERS:
481 *      w               the widget to match
482 *      indx            the index into the resource string
483 *      remainder       the part of the resource string left over
484 *      resource        the resource string to be matched
485 *      value           the value to be set
486 *      last_token      the last * or . before the final resource part
487 *      last_part       the last resource part (e.g. *background)
488 *
489 * RETURN VALUES: none
490 *
491 * ERRORS: none
492 *
493 * ALGORITHM:
494 * loop (look at all children)
495 *      if (resource segment and current widget match)
496 *              if '.'
497 *                      if at end of resource string
498 *                              set values (    .=over all children
499 *                                              *=this widget only)
500 *                      else
501 *                              descend the widget tree
502 *                              and parse off resource segment
503 *                      exit the loop
504 *              if '*'
505 *                      if at end of resource string
506 *                              set values (    .=over all children
507 *                                              *=this widget only)
508 *                      descend and parse
509 *      else
510 *              if '.'
511 *                      continue looping
512 *              if '*'
513 *                      descend but don't parse
514 *                      continue looping
515 * end loop
516 *
517 * NOTE:  the _set_resource_values routine will not allow a value to be
518 *      set on a resource against the rules of the resource database manager
519 */
520static void
521_set_and_search(Widget w,
522                char *indx,
523                char *remainder,
524                char *resource,
525                char *value,
526                char last_token,
527                char *last_part)
528{
529    char *part;
530    char *local_index = indx;
531    char token;
532
533    /*
534     * parse off one part, return token and the new index
535     */
536    token = _get_part(remainder, &local_index, &part);
537
538    if (_match_resource_to_widget(w, part)) {
539        if (token == '.') {
540            if (local_index == NULL) {
541                if (last_token == '.') {
542                    _set_resource_values(w, resource, value, last_part);
543                }
544                else if (last_token == '*') {
545                    _set_resource_values(w, resource, value, last_part);
546                    _apply_values_to_children(w,
547                                              remainder, resource, value,
548                                              last_token, last_part);
549                }
550            }
551            else
552                _search_child(w, local_index, remainder,
553                              resource, value, last_token, last_part);
554            XtFree(part);
555            return;
556        }
557        if (token == '*') {
558            if (local_index == NULL) {
559                if (last_token == '.') {
560                    _set_resource_values(w, resource, value, last_part);
561                }
562                else if (last_token == '*') {
563                    _set_resource_values(w, resource, value, last_part);
564                    _apply_values_to_children(w,
565                                              remainder, resource, value,
566                                              last_token, last_part);
567                }
568            }
569            else
570                _search_child(w, local_index, remainder,
571                              resource, value, last_token, last_part);
572        }
573    }
574    else {                      /* if the widget name and class don't match the part */
575        /* if (token == '.') just continue looping */
576
577        if (token == '*') {
578            _search_child(w, indx, remainder, resource, value,
579                          last_token, last_part);
580        }
581    }
582
583    XtFree(part);
584}
585
586/*
587 * NAME: _get_last_part
588 *
589 * FUNCTION:
590 *      This routine will parse off the last segment of a resource string
591 *      and its token and return them.  the remainder of resource is also
592 *      returned.  strcoll is used to guarantee no problems with
593 *      international strings.
594 *
595 * PARAMETERS:
596 *      remainder       the part of the resource string left over
597 *      part            the parsed off part of the resource string
598 *
599 * RETURN VALUES:
600 *      char            the token (* or . or ?) preceding the resource part
601 *      remainder       the part of the resource string left over
602 *      part            the parsed off part of the resource string
603 *
604 * ERRORS: none
605 */
606static char
607_get_last_part(char *remainder, char **part)
608{
609    char *loose, *tight;
610
611    loose = strrchr(remainder, '*');
612    tight = strrchr(remainder, '.');
613
614    if ((loose == NULL) && (tight == NULL)) {
615        *part = XtNewString(remainder);
616        return ('.');
617    }
618    if ((loose == NULL) || (tight && (strcoll(loose, tight) < 0))) {
619        *tight++ = '\0';        /* shorten the remainder string */
620        *part = XtNewString(tight);
621        return ('.');
622    }
623    if ((tight == NULL) || (strcoll(tight, loose) < 0)) {
624        *loose++ = '\0';
625        *part = XtNewString(loose);
626        return ('*');
627    }
628    *part = NULL;
629
630    return ('0');               /* error - return 0 */
631}
632
633/*
634 * NAME: _search_widget_tree
635 *
636 * FUNCTION:
637 *      This function tries to match a resource string to the widgets
638 *      it applies to.  The functions it invokes to do this then set
639 *      the value for that resource to each widget.
640 *
641 *      The resource string has to be parsed into the following format:
642 *              resource = App*Form*button1.background
643 *              remainder = *Form*button1
644 *              last_part = background          last_token = .
645 *      As the widget tree is recursively descended, these variables are
646 *      passed.  The remainder is parsed at each level in the widget
647 *      tree as the _set_and_search function attempts to match
648 *      the resource part (eg. part = Form  token = *) to a widget.  When
649 *      the entire resource string has been matched, the _set_resource_values
650 *      functions is called to apply the value to the widget or widgets.
651 *
652 * PARAMETERS:
653 *      w               a widget from whose toplevel shell ancestor
654 *                      the search will start
655 *      resource        the resource string to match
656 *      value           the value to apply
657 *
658 * RETURN VALUES: none
659 *
660 * ERRORS: none
661 */
662static void
663_search_widget_tree(Widget w, char *resource, char *value)
664{
665    Widget parent = w;
666    char *last_part;
667    char *remainder = NULL;
668    char *loose, *tight;
669    int loose_len, tight_len;
670
671    if (resource == NULL)
672        return;
673
674    /*
675     * Find the root of the tree given any widget
676     */
677    while (XtParent(parent) != NULL) {
678        parent = XtParent(parent);
679    }
680#ifdef DEBUG
681    if (XtIsWidget(w) && XtIsWidget(parent))
682        fprintf(stderr, "widget = %s parent = %s\n",
683                w->core.name, parent->core.name);
684    else
685        fprintf(stderr, "widget = NULL parent = NULL\n");
686#endif
687
688    /*
689     * parse off the Class name that was prepended to this string in
690     * a customizing tool
691     */
692    loose = strchr(resource, '*');
693    tight = strchr(resource, '.');
694    if ((loose == NULL) && (tight == NULL))
695        return;
696
697    loose_len = (loose) ? (int) strlen(loose) : 0;
698    tight_len = (tight) ? (int) strlen(tight) : 0;
699
700    if ((loose == NULL) || (tight_len > loose_len))
701        remainder = XtNewString(tight);
702    else if ((tight == NULL) || (loose_len > tight_len))
703        remainder = XtNewString(loose);
704
705    /*
706     * Parse last segment off of resource string, (eg. background, font,
707     * etc.)
708     */
709    if (remainder) {
710        char last_token;
711
712        last_token = _get_last_part(remainder, &last_part);
713        /*
714         * this case covers resources of only one level (eg. *background)
715         */
716        if (remainder[0] == 0) {
717            _set_resource_values(w, resource, value, last_part);
718            if (last_token == '*')
719                _apply_values_to_children(parent, remainder, resource,
720                                          value, last_token, last_part);
721            /*
722             * all other resource strings are recursively applied to the widget tree.
723             * Prepend a '.' to the remainder string if there is no leading token.
724             */
725        }
726        else {
727            char *indx, *copy;
728
729            if (remainder[0] != '*' && remainder[0] != '.') {
730                XtAsprintf(&copy, ".%s", remainder);
731                XtFree(remainder);
732                remainder = copy;
733            }
734            indx = remainder;
735            _set_and_search(parent, indx, remainder, resource, value,
736                            last_token, last_part);
737        }
738
739        XtFree(remainder);
740        XtFree(last_part);
741    }
742}
743
744/*
745 * NAME: _locate_children
746 *
747 * FUNCTION:
748 *      returns a list of all of a widget's children
749 *
750 * PARAMETERS:
751 *      w               the parent to search for its children
752 *      children        the list of children that is created
753 *      normal          flag for normal children
754 *      popup           flag for popup children
755 *
756 * RETURN VALUES:
757 *      int             the number of children
758 *      children        the list of children found
759 *
760 * ERRORS: none
761 */
762static int
763_locate_children(Widget parent, Widget **children)
764{
765    CompositeWidget comp = (CompositeWidget) parent;
766    Cardinal i;
767    int num_children = 0;
768    int current = 0;
769
770    /*
771     * count the number of children
772     */
773    if (XtIsWidget(parent))
774        num_children =
775            (int) ((Cardinal) num_children + parent->core.num_popups);
776    if (XtIsComposite(parent))
777        num_children =
778            (int) ((Cardinal) num_children + comp->composite.num_children);
779    if (num_children == 0) {
780        *children = NULL;
781        return (0);
782    }
783
784    *children = XtMallocArray((Cardinal)num_children, (Cardinal)sizeof(Widget));
785
786    if (XtIsComposite(parent)) {
787        for (i = 0; i < comp->composite.num_children; i++) {
788            (*children)[current] = comp->composite.children[i];
789            current++;
790        }
791    }
792
793    if (XtIsWidget(parent)) {
794        for (i = 0; i < parent->core.num_popups; i++) {
795            (*children)[current] = comp->core.popup_list[i];
796            current++;
797        }
798    }
799
800    return (num_children);
801}
802
803#ifdef DEBUG
804/*
805 * NAME: dump_widget_tree
806 *
807 * FUNCTION:
808 *      recursively printout entire widget tree
809 *
810 * PARAMETERS:
811 *      w               the widget to match
812 *      indent          the amount to indent each line
813 *
814 * RETURN VALUES: void
815 *
816 * ERRORS: none
817 */
818static void
819dump_widget_tree(Widget w, int indent)
820{
821    int i, j;
822    int num_children;
823    Widget *children;
824
825    /*
826     * Recursively search through the children
827     */
828    num_children = _locate_children(w, &children);
829    indent += 2;
830    for (i = 0; i < num_children; i++) {
831        if (children[i] != NULL) {
832            for (j = 0; j < indent; j++)
833                fprintf(stderr, " ");
834            if (XtIsWidget(children[i])) {
835                fprintf(stderr, "(%s)\t", children[i]->core.name);
836                fprintf(stderr, "(%s)\n",
837                        children[i]->core.widget_class->core_class.class_name);
838            }
839            else {
840                fprintf(stderr, "(NULL)\t");
841                fprintf(stderr, "(%s)\n",
842                        children[i]->core.widget_class->core_class.class_name);
843            }
844        }
845        dump_widget_tree(children[i], indent);
846    }
847
848    XtFree((char *) children);
849}
850#endif
851
852/*
853 * NAME: _XtResourceConfiguationEH
854 *
855 * FUNCTION:
856 *      This function is the event handler for the on-the-fly communication
857 *      with a resource customization tool.  This event handler must be
858 *      registered for the toplevel shell of each app.  This is best done
859 *      in the _XtCreatePopupShell and _XtAppCreateShell functions in Xt's
860 *      Create.c source file.
861 *
862 *      The property used to communicate with a customizing tool is
863 *      placed on the toplevel shell window of the application.  The
864 *      customizing tool places a property on this window which causes
865 *      this event handler to be invoked via the PropertyNotify event.
866 *      This event handler reads the property and then deletes it from
867 *      the server.  The contents of the property are a resource string
868 *      and value.  The event handler then calls functions to walk the
869 *      applications widget tree, determining which widgets are affected
870 *      by the resource string, and then applying the value with XtSetValues.
871 *
872 * PARAMETERS:
873 *      w               the widget that invoked this event handler
874 *      client_data     not used
875 *      event           the event structure
876 *
877 * RETURN VALUES: none
878 *
879 * ERRORS: none
880 */
881void
882_XtResourceConfigurationEH(Widget w,
883                           XtPointer client_data _X_UNUSED,
884                           XEvent *event,
885                           Boolean *continue_to_dispatch _X_UNUSED)
886{
887    Atom actual_type;
888    int actual_format;
889    unsigned long nitems;
890    unsigned long leftover;
891    char *data = NULL;
892    char *data_ptr;
893
894#ifdef DEBUG
895    int indent = 0;
896#endif
897    XtPerDisplay pd;
898
899#ifdef DEBUG
900    fprintf(stderr, "in _XtResourceConfiguationEH atom = %u\n",
901            (unsigned) event->xproperty.atom);
902    fprintf(stderr, "    window = %x\n", (unsigned) XtWindow(w));
903    if (XtIsWidget(w))
904        fprintf(stderr, "    widget = %zx   name = %s\n", (size_t) w,
905                w->core.name);
906#endif
907
908    pd = _XtGetPerDisplay(XtDisplay(w));
909
910    /*
911     * The window on which a customizing tool places the property
912     * is determined at this point.  It should be the applications
913     * toplevel shell window.
914     *
915     * A customizing tool sends a "ping" to the application on
916     * the RCM_INIT property.  The application answers the ping
917     * by deleting the property.
918     */
919    if (event->xproperty.atom == pd->rcm_init) {
920        XDeleteProperty(XtDisplay(w), XtWindow(w), pd->rcm_init);
921
922#ifdef DEBUG
923        if (XtIsWidget(w))
924            fprintf(stderr, "%s\n", w->core.name);
925        else
926            fprintf(stderr, "NULL name\n");
927        dump_widget_tree(w, indent);
928
929        fprintf(stderr, "answer ping\n");
930#endif
931    }
932
933    /*
934     * This event handler ignores any property notify events that
935     * are not RCM_INIT or RCM_DATA
936     */
937    if (event->xproperty.atom != pd->rcm_data)
938        return;
939
940    /*
941     * Retrieve the data from the property
942     */
943#ifdef DEBUG
944    fprintf(stderr, "receiving RCM_DATA property\n");
945#endif
946    if (XGetWindowProperty(XtDisplay(w),
947                           XtWindow(w),
948                           pd->rcm_data, 0L, 8192L,
949                           TRUE, XA_STRING,
950                           &actual_type, &actual_format, &nitems, &leftover,
951                           (unsigned char **) &data) == Success &&
952        actual_type == XA_STRING && actual_format == 8) {
953        /*
954         *      data format is:
955         *
956         *      resource_length, resource, value
957         *
958         *      convert the resource_length to a long, skip over it, put a
959         *      zero byte at the end of the resource, and pick off the
960         *      resource and value fields.
961         */
962        if (data) {
963            char *data_end = data + nitems;
964            char *data_value;
965            unsigned long resource_len;
966
967            resource_len = strtoul(data, &data_ptr, 10);
968
969            if (data_ptr != (char *) data) {
970                data_ptr++;
971                data_value = data_ptr + resource_len;
972            }
973            else                /* strtoul failed to convert a number */
974                data_ptr = data_value = NULL;
975
976            if (data_value > data_ptr && data_value < data_end) {
977                char *resource;
978                char *value;
979
980                *data_value++ = '\0';
981
982                resource = XtNewString(data_ptr);
983                value = XtNewString(data_value);
984#ifdef DEBUG
985                fprintf(stderr, "resource_len=%lu\n", resource_len);
986                fprintf(stderr, "resource = %s\t value = %s\n",
987                        resource, value);
988#endif
989                /*
990                 * descend the application widget tree and
991                 * apply the value to the appropriate widgets
992                 */
993                _search_widget_tree(w, resource, value);
994
995                XtFree(resource);
996                XtFree(value);
997            }
998        }
999    }
1000
1001    XFree((char *) data);
1002}
1003