1/*
2 *
3Copyright 1989, 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#ifdef HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include <X11/Intrinsic.h>
31#include <X11/Xutil.h>
32#include <X11/Xos.h>
33#include <X11/Shell.h>
34#include <X11/StringDefs.h>
35
36#include <X11/Xaw/Cardinals.h>
37#include <X11/Xaw/Dialog.h>
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <X11/Xmu/Error.h>
42
43#include "editresP.h"
44
45static WNode * FindWidgetFromWindowGivenNode ( WNode * node, Window win );
46static WidgetResources * ParseResources ( GetResourcesInfo * info,
47					  char **error );
48static int CompareResourceEntries ( const void *e1,
49				    const void *e2 );
50static void AddResource ( ResourceInfo * res_info,
51			  WidgetResourceInfo * resource );
52static void FreeResources ( WidgetResources * resources );
53
54
55/*	Function Name: SetMessage(w, str)
56 *	Description: shows the message to the user.
57 *	Arguments: w - a label widget to show the message in.
58 *                 str - the string to show.
59 *	Returns: none.
60 */
61
62void
63SetMessage(Widget w, const char *str)
64{
65    Arg args[1];
66
67    XtSetArg(args[0], XtNlabel, str);
68    XtSetValues(w, args, ONE);
69}
70
71/*	Function Name: GetAllStrings
72 *	Description: Returns a list of strings that have been broken up by
73 *                   the character specified.
74 *	Arguments: in - the string to parse.
75 *                 sep - the separator character.
76 *                 out - the strings to send out.
77 *                 num - the number of strings in out.
78 *	Returns: none
79 */
80
81void
82GetAllStrings(char *in, char sep, char ***out, int *num)
83{
84    int size, i;
85    char * ptr;
86
87    if (*in == sep)		/* jump over first char if it is the sep. */
88	in++;
89
90    /*
91     * count the number of strings.
92     */
93
94    for (*num = 1, ptr = in; (ptr = strchr(ptr, sep)) != NULL; (*num)++)
95	ptr++;
96
97/*
98 * Create Enough space for pointers and string.
99 */
100
101    size = (sizeof(char *) * *num) + (sizeof(char) * (strlen(in) + 1));
102    *out = (char **) XtMalloc( (Cardinal) size);
103
104    ptr = (char *) (*out + *num);
105    strcpy(ptr, in);
106
107/*
108 * Change all `sep' characters to '\0' and stuff the pointer into
109 * the next pointer slot.
110 */
111
112    i = 1;
113    (*out)[0] = ptr;
114    while (TRUE) {
115	if ((ptr = strchr(ptr, sep)) == NULL)
116	    break;
117
118	*ptr++ = '\0';
119	(*out)[i++] = ptr;
120    }
121
122/*
123 * If last string is empty then strip it off.
124 */
125
126    if ( *((*out)[i - 1]) == '\0' )
127	(*num)--;
128}
129
130/*	Function Name: AddString
131 *	Description: Mallocs and strcats the string onto the end of
132 *                   the given string.
133 *	Arguments: str - string to add on to.
134 *                 add - string to add.
135 *	Returns: none.
136 */
137
138void
139AddString(char ** str, const char *add)
140{
141    int len_str, len_add;
142    char * ptr;
143
144    len_str = ((*str) ? strlen(*str) : 0);
145    len_add = strlen(add);
146
147    *str = XtRealloc(*str, sizeof(char) * (len_str + len_add + 1));
148    ptr = *str + len_str;
149    strcpy(ptr, add);
150}
151
152/*	Function Name: FindNode
153 *	Description: Finds a node give the top node, and a node id number.
154 *	Arguments: top_node - the top node.
155 *                 id - the node id.
156 *	Returns: node.
157 */
158
159WNode *
160FindNode(WNode *top_node, unsigned long *ids, Cardinal number)
161{
162    Cardinal i, j;
163    WNode *node;
164
165    if (top_node == NULL)
166	return(NULL);
167
168    if (ids[0] != top_node->id)
169	return(NULL);
170
171    for (node = top_node, i = 1 ; i < number; i++) {
172	Boolean found_it = FALSE;
173
174	for (j = 0; j < node->num_children; j++) {
175	    if (node->children[j]->id == ids[i]) {
176		node = node->children[j];
177		found_it = TRUE;
178		break;
179	    }
180	}
181	if (!found_it)
182	    return(NULL);
183    }
184    return(node);
185}
186
187/*	Function Name: FindWidgetFromWindow
188 *	Description: finds a widget in the current tree given its window id.
189 *	Arguments: tree_info - information about this tree.
190 *                 win - window to search for.
191 *	Returns: node - the node corrosponding to this widget.
192 */
193
194WNode *
195FindWidgetFromWindow(TreeInfo *tree_info, Window win)
196{
197    if (tree_info == NULL)
198	return(NULL);
199
200    return(FindWidgetFromWindowGivenNode(tree_info->top_node, win));
201}
202
203/*	Function Name: FindWidgetFromWindowGivenNode
204 *	Description: finds a widget in the current tree given its window id.
205 *	Arguments: node - current node.
206 *                 win - window to search for.
207 *	Returns: node - the node corrosponding to this widget.
208 */
209
210static WNode *
211FindWidgetFromWindowGivenNode(WNode *node, Window win)
212{
213    Cardinal i;
214    WNode * ret_node;
215
216    if (node->window == win)
217	return(node);
218
219    for (i = 0; i < node->num_children; i++) {
220	ret_node = FindWidgetFromWindowGivenNode(node->children[i], win);
221	if (ret_node != NULL)
222	    return(ret_node);
223    }
224    return(NULL);
225}
226
227/*	Function Name: HandleXErrors
228 *	Description: Handles error codes from the server.
229 *	Arguments: display - the display.
230 *                 error - error information.
231 *	Returns: none.
232 */
233
234/* ARGSUSED */
235int
236HandleXErrors(Display *display, XErrorEvent *error)
237{
238    if (error->serial != global_serial_num) {
239	(*global_old_error_handler) (display, error);
240	return(0);
241    }
242
243    if (error->error_code == BadWindow)
244	global_error_code = NO_WINDOW;
245    else {
246	if (XmuPrintDefaultErrorMessage(display, error, stderr) != 0)
247	    exit(1);
248    }
249    return(0);
250}
251
252/*	Function Name: _DumpTreeToFile
253 *	Description: Dumps the widget tree to a file
254 *	Arguments: w - a random widget in the application on the
255 *                     currently active display
256 *                 tree_ptr - pointer to the widget tree info.
257 *                 filename - name of the file.
258 *	Returns: none.
259 */
260
261/* ARGSUSED */
262
263void
264_DumpTreeToFile(Widget w, XtPointer tree_ptr, XtPointer filename)
265{
266    TreeInfo * tree_info = (TreeInfo *) tree_ptr;
267    FILE * fp;
268
269    if (tree_info == NULL) {
270	SetMessage(global_screen_data.info_label,
271		   res_labels[17]);
272	return;
273    }
274
275    if ( (fp = fopen((char *)filename, "w")) == NULL ) {
276	char buf[BUFSIZ];
277
278	snprintf(buf, sizeof(buf), res_labels[24], (char *)filename);
279	SetMessage(global_screen_data.info_label, buf);
280	return;
281    }
282
283    PerformTreeToFileDump(tree_info->top_node, 0, fp);
284    fclose(fp);
285}
286
287/************************************************************
288 *
289 * The file dialog boxes are handled with this code.
290 *
291 * It automatically calls the function specified when the
292 * user selects okay, or hits <CR>.
293 *
294 * A translation is required in the app-defaults file.
295 *
296 ************************************************************/
297
298/*	Function Name: _PopupFileDialog
299 *	Description: Puts up a dialog box to get the filename.
300 *	Arguments: str - message.
301 *                 default_value - the default value of the filename;
302 *                 func - function to call when filename has been entered.
303 *                 data - generic data to pass to func.
304 *	Returns: none
305 */
306
307static XContext file_dialog_context = None;
308
309typedef struct _FileDialogInfo {
310    XtCallbackProc func;
311    XtPointer data;
312} FileDialogInfo;
313
314void
315_PopupFileDialog(Widget w, const char *str, const char *default_value,
316		 XtCallbackProc func, XtPointer data)
317{
318    FileDialogInfo * file_info;
319    Widget shell, dialog;
320    Arg args[2];
321    Cardinal num_args;
322
323    if (file_dialog_context == None)
324	file_dialog_context = XUniqueContext();
325
326    shell = XtCreatePopupShell("fileDialog", transientShellWidgetClass, w,
327			       NULL, ZERO);
328
329    num_args = 0;
330    XtSetArg(args[num_args], XtNlabel, str); num_args++;
331    XtSetArg(args[num_args], XtNvalue, default_value); num_args++;
332    dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
333				   shell, args, num_args);
334
335    file_info = XtNew(FileDialogInfo);
336
337    file_info->func = func;
338    file_info->data = data;
339
340    if  (XSaveContext(XtDisplay(dialog), (Window) dialog, file_dialog_context,
341		      (XPointer) file_info) != 0) {
342	SetMessage(global_screen_data.info_label,
343	    "Error while trying to save Context\nAborting file dialog popup.");
344	XtDestroyWidget(shell);
345	return;
346    }
347
348    XawDialogAddButton(dialog, "okay", _PopdownFileDialog, (XtPointer) TRUE);
349    XawDialogAddButton(dialog, "cancel", _PopdownFileDialog,(XtPointer) FALSE);
350
351    PopupCentered(NULL, shell, XtGrabNone);
352}
353
354/*	Function Name: PopupCentered
355 *	Description: Pops up the window specified under the location passed
356 *                   in the event, or under the cursor.
357 *	Arguments: event - the event that we should use.
358 *                 w - widget to popup.
359 *                 mode - mode to pop it up in.
360 *	Returns: none
361 */
362
363void
364PopupCentered(XEvent *event, Widget w, XtGrabKind mode)
365{
366    Boolean get_from_cursor = FALSE;
367    Arg args[3];
368    Cardinal num_args;
369    Dimension width, height, b_width;
370    int x, y, max_x, max_y;
371
372    XtRealizeWidget(w);
373
374    if (event == NULL)
375	get_from_cursor = TRUE;
376    else {
377	switch (event->type) {
378	case ButtonPress:
379	case ButtonRelease:
380	    x = event->xbutton.x_root;
381	    y = event->xbutton.y_root;
382	    break;
383	case KeyPress:
384	case KeyRelease:
385	    x = event->xkey.x_root;
386	    y = event->xkey.y_root;
387	    break;
388	default:
389	    get_from_cursor = TRUE;
390	    break;
391	}
392    }
393
394    if (get_from_cursor) {
395	Window root, child;
396	int win_x, win_y;
397	unsigned int mask;
398
399	XQueryPointer(XtDisplay(w), XtWindow(w),
400		      &root, &child, &x, &y, &win_x, &win_y, &mask);
401    }
402
403    num_args = 0;
404    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
405    XtSetArg(args[num_args], XtNheight, &height); num_args++;
406    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
407    XtGetValues(w, args, num_args);
408
409    width += 2 * b_width;
410    height += 2 * b_width;
411
412    x -= ((int) width/2);
413    if (x < 0)
414	x = 0;
415    if ( x > (max_x = (int) (XtScreen(w)->width - width)) )
416	x = max_x;
417
418    y -= ( (Position) height/2 );
419    if (y < 0)
420	y = 0;
421    if ( y > (max_y = (int) (XtScreen(w)->height - height)) )
422	y = max_y;
423
424    num_args = 0;
425    XtSetArg(args[num_args], XtNx, x); num_args++;
426    XtSetArg(args[num_args], XtNy, y); num_args++;
427    XtSetValues(w, args, num_args);
428
429    XtPopup(w, mode);
430}
431
432/*	Function Name: _PopdownFileDialog
433 *	Description: Destroys the file dialog, and calls the correct function.
434 *	Arguments:  w - a child of the dialog widget.
435 *                  client_data - TRUE if command was successful.
436 *                  junk - ** UNUSED **.
437 *	Returns: none.
438 */
439
440/* ARGSUSED */
441
442void
443_PopdownFileDialog(Widget w, XtPointer client_data, XtPointer junk)
444{
445    Widget dialog = XtParent(w);
446    XPointer file_info_ptr;
447    FileDialogInfo * file_info;
448
449    if (XFindContext(XtDisplay(dialog), (Window) dialog, file_dialog_context,
450		     &file_info_ptr) == XCNOENT) {
451	SetMessage(global_screen_data.info_label,
452		   "Error while trying to find Context\nAborting...");
453    }
454
455    (void) XDeleteContext(XtDisplay(dialog), (Window)dialog,
456			  file_dialog_context);
457
458    file_info = (FileDialogInfo *) file_info_ptr;
459
460    if ( ((Boolean)(long) client_data) == TRUE ) {
461	String filename = XawDialogGetValueString(dialog);
462
463	(*file_info->func)(w, file_info->data, filename); /* call handler */
464    }
465
466    XtFree( (XtPointer) file_info); /* Free data. */
467
468    XtPopdown(XtParent(dialog));
469    XtDestroyWidget(XtParent(dialog)); /* Remove file dialog. */
470}
471
472/************************************************************
473 *
474 * Functions for dealing with the Resource Box.
475 *
476 ************************************************************/
477
478/*    Function Name: GetNamesAndClasses
479 *    Description: Gets a list of names and classes for this widget.
480 *    Arguments: node - this widget's node.
481 *                 names, classes - list of names and classes. ** RETURNED **
482 *    Returns: none.
483 */
484
485void
486GetNamesAndClasses(WNode *node, char ***names, char ***classes)
487{
488    int i, total_widgets;
489    WNode * temp = node;
490
491    for (total_widgets = 1 ; temp->parent != NULL ;
492       total_widgets++, temp = temp->parent) {}
493
494    *names = (char **) XtMalloc(sizeof(char *) * (total_widgets + 1));
495    *classes = (char **) XtMalloc(sizeof(char *) * (total_widgets + 1));
496
497    (*names)[total_widgets] = (*classes)[total_widgets] = NULL;
498
499    for ( i = (total_widgets - 1); i >= 0 ; node = node->parent, i--) {
500      (*names)[i] = node->name;
501      (*classes)[i] = node->class;
502    }
503}
504
505/*	Function Name: HandleGetResources
506 *	Description: Gets the resources.
507 *	Arguments: event - the information from the client.
508 *	Returns: an error message to display.
509 */
510
511char *
512HandleGetResources(Event *event)
513{
514    GetResourcesEvent * get_event = (GetResourcesEvent *) event;
515    char buf[BUFSIZ], * errors = NULL;
516    int i;
517    WNode * node;
518
519    for (i = 0; i < (int)get_event->num_entries; i++) {
520	node = FindNode(global_tree_info->top_node,
521			get_event->info[i].widgets.ids,
522			get_event->info[i].widgets.num_widgets);
523
524	if (node == NULL) {
525	    snprintf(buf, sizeof(buf), res_labels[16]);
526	    AddString(&errors, buf);
527	    continue;
528	}
529
530	if (node->resources != NULL)
531	    FreeResources(node->resources);
532
533	if (!get_event->info[i].error) {
534	    node->resources = ParseResources(get_event->info + i, &errors);
535	    CreateResourceBox(node, &errors);
536	}
537	else {
538	    AddString(&errors, get_event->info[i].message);
539	    AddString(&errors, "\n");
540	}
541    }
542
543    return(errors);
544}
545
546/*	Function Name: CreateResourceBox
547 *	Description: Creates a resource box for the widget specified.
548 *	Arguments: node - the node of the widget in question.
549 *                 errors - an error string.
550 *	Returns: none.
551 */
552
553void
554CreateResourceBox(WNode *node, char **errors)
555{
556    WidgetResources * resources = node->resources;
557    char ** names, ** cons_names;
558    int i;
559
560    if (global_resource_box_up) {
561	AddString(errors, res_labels[34]);
562	return;
563    }
564    else
565	global_resource_box_up = TRUE;
566
567    if (resources->num_normal > 0) {
568	names = (char **) XtMalloc(sizeof(char *) *
569				   (resources->num_normal + 1));
570	for (i = 0 ; i < resources->num_normal ; i++)
571	    names[i] = resources->normal[i].name;
572	names[i] = NULL;
573    }
574    else
575	names = NULL;
576
577    if (resources->num_constraint > 0) {
578	cons_names = (char **) XtMalloc(sizeof(char *) *
579					(resources->num_constraint + 1));
580
581	for (i = 0 ; i < resources->num_constraint ; i++)
582	    cons_names[i] = resources->constraint[i].name;
583	cons_names[i] = NULL;
584    }
585    else
586	cons_names = NULL;
587
588    CreateResourceBoxWidgets(node, names, cons_names);
589}
590
591/*	Function Name: ParseResources
592 *	Description: Parses the resource values returned from the client
593 *                   into a resources structure.
594 *	Arguments: info - info about a widget's resources.
595 *                 error - where to place error info.
596 *	Returns: The resource information.
597 */
598
599static WidgetResources *
600ParseResources(GetResourcesInfo *info, char **error)
601{
602    WidgetResources * resources;
603    WidgetResourceInfo * normal;
604    int i;
605
606    resources = (WidgetResources *) XtMalloc(sizeof(WidgetResources));
607
608    /*
609     * Allocate enough space for both the normal and constraint resources,
610     * then add the normal resources from the top, and the constraint resources
611     * from the bottom.  This assures that enough memory is allocated, and
612     * that there is no overlap.
613     */
614
615    resources->normal = (WidgetResourceInfo *)
616	            XtMalloc(sizeof(WidgetResourceInfo) * info->num_resources);
617
618    normal = resources->normal;
619    resources->constraint = resources->normal + info->num_resources - 1;
620
621    resources->num_constraint = resources->num_normal = 0;
622
623    for (i = 0; i < (int)info->num_resources; i++) {
624	switch((int) info->res_info[i].res_type) {
625	case NormalResource:
626	    resources->num_normal++;
627	    AddResource(info->res_info + i, normal++);
628	    break;
629	case ConstraintResource:
630	    resources->num_constraint++;
631	    AddResource(info->res_info + i, resources->constraint--);
632	    break;
633	default:
634	    {
635		char buf[BUFSIZ];
636		snprintf(buf, sizeof(buf), "Unknown resource type %d\n",
637                         info->res_info[i].res_type);
638		AddString(error, buf);
639	    }
640	    break;
641	}
642    }
643
644    /*
645     * Sort the resources alphabetically.
646     */
647
648    qsort(resources->normal, resources->num_normal,
649	  sizeof(WidgetResourceInfo), CompareResourceEntries);
650
651    if (resources->num_constraint > 0) {
652	resources->constraint++;
653	qsort(resources->constraint, resources->num_constraint,
654	      sizeof(WidgetResourceInfo), CompareResourceEntries);
655    }
656    else
657	resources->constraint = NULL;
658
659    return(resources);
660}
661
662/*	Function Name: CompareResourceEntries
663 *	Description: Compares two resource entries.
664 *	Arguments: e1, e2 - the entries to compare.
665 *	Returns: an integer >, < or = 0.
666 */
667
668static int
669CompareResourceEntries(const void *e1, const void *e2)
670{
671    return (strcmp(((WidgetResourceInfo *)e1)->name,
672		   ((WidgetResourceInfo *)e2)->name));
673}
674
675/*	Function Name: AddResource
676 *	Description: Parses the resource string a stuffs in individual
677 *                   parts into the resource info struct.
678 *	Arguments: res_info - the resource info from the event.
679 *                 resource - location to stuff the resource into.
680 *	Returns: none.
681 */
682
683static void
684AddResource(ResourceInfo *res_info, WidgetResourceInfo *resource)
685{
686    resource->name = res_info->name;
687    res_info->name = NULL;	/* Keeps it from being deallocated. */
688    resource->class = res_info->class;
689    res_info->class = NULL;	/* Keeps it from being deallocated. */
690    resource->type = res_info->type;
691    res_info->type = NULL;	/* Keeps it from being deallocated. */
692}
693
694
695/*	Function Name: FreeResources
696 *	Description: frees the resource information.
697 *	Arguments: resources.
698 *	Returns: none.
699 */
700
701static void
702FreeResources(WidgetResources *resources)
703{
704    int i;
705
706    if (resources->num_normal > 0) {
707	for (i = 0; i < resources->num_normal; i++) {
708	    XtFree(resources->normal[i].name);
709	    XtFree(resources->normal[i].class);
710	    XtFree(resources->normal[i].type);
711	}
712	XFree((char *)resources->normal);
713    }
714
715    if (resources->num_constraint > 0) {
716	for (i = 0; i < resources->num_constraint; i++) {
717	    XtFree(resources->constraint[i].name);
718	    XtFree(resources->constraint[i].class);
719	    XtFree(resources->constraint[i].type);
720	}
721	XFree((char *)resources->constraint);
722    }
723
724    XFree((char *)resources);
725}
726
727
728/*	Function Name: CheckDatabase
729 *	Description: Checks to see if the node is in the database.
730 *	Arguments: db - the db to check
731 *                 names, classes - names and classes, represented as quarks.
732 *	Returns: True if this entry is found.
733 */
734
735Boolean
736CheckDatabase(XrmDatabase db, XrmQuarkList names, XrmQuarkList classes)
737{
738    XrmRepresentation junk;
739    XrmValue garbage;
740
741    return(XrmQGetResource(db, names, classes, &junk, &garbage));
742}
743
744/*	Function Name: Quarkify
745 *	Description: Quarkifies the string list specified.
746 *	Arguments: list - list of strings to quarkify
747 *                 ptr - an additional string to quarkify.
748 *	Returns: none.
749 */
750
751XrmQuarkList
752Quarkify(char **list, const char *ptr)
753{
754    int i;
755    char ** tlist;
756    XrmQuarkList quarks, tquarks;
757
758    for (i = 0, tlist = list; *tlist != NULL; tlist++, i++) {}
759    if (ptr != NULL)
760	i++;
761    i++;			/* leave space for NULLQUARK */
762
763    quarks = (XrmQuarkList) XtMalloc(sizeof(XrmQuark) * i);
764
765    for (tlist = list, tquarks = quarks; *tlist != NULL; tlist++, tquarks++)
766	*tquarks = XrmStringToQuark(*tlist);
767
768    if (ptr != NULL)
769	*tquarks++ = XrmStringToQuark(ptr);
770
771    *tquarks = NULLQUARK;
772    return(quarks);
773}
774
775/*	Function Name: ExecuteOverAllNodes
776 *	Description: Executes the given function over all nodes.
777 *	Arguments: top_node - top node of the tree.
778 *                 func - the function to execute.
779 *                 data - a data pointer to pass to the function.
780 *	Returns: none
781 */
782
783void
784ExecuteOverAllNodes(WNode *top_node, void (*func)(WNode *, XtPointer),
785		    XtPointer data)
786{
787    Cardinal i;
788
789    (*func)(top_node, data);
790
791    for (i = 0; i < top_node->num_children; i++)
792	ExecuteOverAllNodes(top_node->children[i], func, data);
793}
794
795/*	Function Name: InsertWidgetFromNode
796 *	Description: Inserts the widget info for this widget represented
797 *                   by this node.
798 *	Arguments: stream - the stream to insert it info into.
799 *                 none - the widget node to insert.
800 *	Returns: none
801 */
802
803void
804InsertWidgetFromNode(ProtocolStream *stream, WNode *node)
805{
806    WNode *temp;
807    unsigned long * widget_list;
808    register int i, num_widgets;
809
810    for (temp = node, i = 0; temp != NULL; temp = temp->parent, i++) {}
811
812    num_widgets = i;
813    widget_list = (unsigned long *)
814	          XtMalloc(sizeof(unsigned long) * num_widgets);
815
816    /*
817     * Put the widgets into the list.
818     * Make sure that they are inserted in the list from parent -> child.
819     */
820
821    for (i--, temp = node; temp != NULL; temp = temp->parent, i--)
822	widget_list[i] = temp->id;
823
824    _XEditResPut16(stream, num_widgets);	/* insert number of widgets. */
825    for (i = 0; i < num_widgets; i++) 	/* insert Widgets themselves. */
826	_XEditResPut32(stream, widget_list[i]);
827
828    XtFree((char *)widget_list);
829}
830
831/*	Function Name: GetFailureMesssage
832 *	Description: returns the message returned from a failed request.
833 *	Arguments: stream - the protocol stream containing the message.
834 *	Returns: message to show.
835 */
836
837char *
838GetFailureMessage(ProtocolStream *stream)
839{
840    char * return_str;
841
842    if (_XEditResGetString8(stream, &return_str))
843	return(return_str);
844
845    return(XtNewString(res_labels[35]));
846}
847
848/*	Function Name: ProtocolFailure
849 *	Description: Gets the version of the protocol the client is
850 *                   willing to speak.
851 *	Arguments: stream - the protocol stream containing the message.
852 *	Returns: message to show.
853 */
854
855char *
856ProtocolFailure(ProtocolStream *stream)
857{
858    char buf[BUFSIZ];
859    unsigned char version;
860    const char* old_version_string;
861
862    if (!_XEditResGet8(stream, &version))
863	return(XtNewString(res_labels[35]));
864
865    switch ((int)version) {
866    case PROTOCOL_VERSION_ONE_POINT_ZERO: old_version_string = "1.0"; break;
867    default: old_version_string = "1.0";
868    }
869
870    snprintf(buf, sizeof(buf), res_labels[36],
871             CURRENT_PROTOCOL_VERSION_STRING, old_version_string);
872    return(XtNewString(buf));
873}
874
875