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 <stdio.h>
31#include <X11/Intrinsic.h>
32#include <X11/Xutil.h>
33#include <X11/StringDefs.h>
34
35#include <X11/Xaw/Cardinals.h>
36#include <X11/Xaw/Toggle.h>
37#include <X11/Xaw/Viewport.h>
38#include <X11/Xaw/Tree.h>
39
40#include "editresP.h"
41
42static void AddNodeToActiveList ( WNode * node );
43static void RemoveNodeFromActiveList ( WNode * node );
44static Boolean IsActiveNode ( WNode * node );
45static void AddNode ( WNode ** top_node, WidgetTreeInfo * info,
46		      TreeInfo * tree_info );
47static void FillNode ( WidgetTreeInfo * info, WNode * node,
48		       TreeInfo * tree_info );
49static void AddChild ( WNode * parent, WNode * child );
50static WNode ** CopyActiveNodes ( TreeInfo * tree_info );
51
52/*	Function Name: BuildVisualTree
53 *	Description: Creates the Tree and shows it.
54 *	Arguments: tree_parent - parent of the tree widget.
55 *                 event - the event that caused this action.
56 *	Returns: none.
57 */
58
59/* ARGSUSED */
60void
61BuildVisualTree(Widget tree_parent, Event *event)
62{
63    WNode * top;
64    char msg[BUFSIZ];
65
66    if (global_tree_info != NULL) {
67	XtDestroyWidget(global_tree_info->tree_widget);
68	XtFree((char *)global_tree_info->active_nodes);
69	XtFree((char *)global_tree_info);
70    }
71
72    global_tree_info = CreateTree(event);
73    top = global_tree_info->top_node;
74
75    global_tree_info->tree_widget = XtCreateWidget("tree", treeWidgetClass,
76						   tree_parent, NULL, ZERO);
77
78    if (top == NULL) {
79	SetMessage(global_screen_data.info_label,
80		   res_labels[27]);
81	return;
82    }
83
84    AddTreeNode(global_tree_info->tree_widget, top);
85
86    if (XtIsRealized(tree_parent)) /* hack around problems in Xt. */
87	XtRealizeWidget(global_tree_info->tree_widget);
88
89    XtManageChild(global_tree_info->tree_widget);
90
91    snprintf(msg, sizeof(msg), res_labels[11], top->name, top->class);
92    SetMessage(global_screen_data.info_label, msg);
93}
94
95/*	Function Name: AddTreeNode
96 *	Description: Adds all nodes below this to the Tree widget.
97 *	Arguments: parent - parent of the tree widget.
98 *                 top - the top node of the tree.
99 *	Returns: the tree widget.
100 *
101 * NOTE: This is a recursive function.
102 */
103
104void
105AddTreeNode(Widget tree, WNode *top)
106{
107    Cardinal i;
108    Arg args[1];
109    Cardinal num_args = 0;
110    char msg[BUFSIZ];
111
112    if (top->parent != NULL) {
113	if (top->parent->widget == NULL) {
114	    snprintf(msg, sizeof(msg), res_labels[28],
115                     top->name, top->parent->name, "not been created yet");
116	    SetMessage(global_screen_data.info_label, msg);
117	}
118	XtSetArg(args[num_args], (String)XtNtreeParent, top->parent->widget);
119	num_args++;
120    }
121
122    top->widget = XtCreateManagedWidget(top->name, toggleWidgetClass, tree,
123					args, num_args);
124
125    if (XSaveContext(XtDisplay(top->widget), (Window) top->widget,
126		     NODE_INFO, (XPointer) top) != 0) {
127	snprintf(msg, sizeof(msg), res_labels[29], top->name);
128	SetMessage(global_screen_data.info_label, msg);
129    }
130
131    XtAddCallback(top->widget, XtNcallback, TreeToggle, (XtPointer) top);
132
133    for (i = 0; i < top->num_children; i++)
134	AddTreeNode(tree, top->children[i]);
135}
136
137/*	Function Name: TreeToggle
138 *	Description: Called whenever a tree node is toggled.
139 *	Arguments: w - the tree widget.
140 *                 node_ptr - pointer to this node's information.
141 *                 state_ptr - state of the toggle.
142 *	Returns: none.
143 */
144
145/* ARGSUSED */
146void
147TreeToggle(Widget w, XtPointer node_ptr, XtPointer state_ptr)
148{
149    Boolean state = (Boolean)(long) state_ptr;
150    WNode * node = (WNode *) node_ptr;
151
152    if (state)
153	AddNodeToActiveList(node);
154    else
155	RemoveNodeFromActiveList(node);
156}
157
158/*	Function Name: AddNodeToActiveList
159 *	Description: Adds this node to the list of active toggles.
160 *	Arguments: node - node to add.
161 *	Returns: none.
162 */
163
164static void
165AddNodeToActiveList(WNode *node)
166{
167    TreeInfo * info = node->tree_info;
168
169    if (IsActiveNode(node))	/* node already active. */
170	return;
171
172    if (info->num_nodes >= info->alloc_nodes) {
173	info->alloc_nodes += NUM_INC;
174	info->active_nodes =(WNode **)XtRealloc((XtPointer) info->active_nodes,
175						sizeof(WNode *) *
176						     info->alloc_nodes);
177    }
178
179    info->active_nodes[info->num_nodes++] = node;
180}
181
182/*	Function Name: RemoveNodeFromActiveList
183 *	Description: Removes a node from the active list.
184 *	Arguments: node - node to remove.
185 *	Returns: none.
186 */
187
188static void
189RemoveNodeFromActiveList(WNode *node)
190{
191    TreeInfo * info = node->tree_info;
192    Boolean found_node = FALSE;
193    Cardinal i;
194
195    if (!IsActiveNode(node))	/* This node is not active. */
196	return;
197
198    for (i = 0; i < info->num_nodes; i++) {
199	if (found_node)
200	    info->active_nodes[i - 1] = info->active_nodes[i];
201	else if (info->active_nodes[i] == node)
202	    found_node = TRUE;
203    }
204
205    info->num_nodes--;
206}
207
208/*	Function Name: IsActiveNode
209 *	Description: returns TRUE is this node is on the active list.
210 *	Arguments: node - node to check.
211 *	Returns: see above.
212 */
213
214static Boolean
215IsActiveNode(WNode *node)
216{
217    TreeInfo * info = node->tree_info;
218    Cardinal i;
219
220    for (i = 0; i < info->num_nodes; i++)
221	if (info->active_nodes[i] == node)
222	    return(TRUE);
223
224    return(FALSE);
225}
226
227/*	Function Name: CreateTree
228 *	Description: Creates a widget tree give a list of names and classes.
229 *	Arguments: event - the information from the client.
230 *	Returns: The tree_info about this new tree.
231 */
232
233TreeInfo *
234CreateTree(Event *event)
235{
236    SendWidgetTreeEvent * send_event = (SendWidgetTreeEvent *) event;
237    unsigned short i;
238
239    TreeInfo * tree_info;
240
241    tree_info = (TreeInfo *) XtMalloc( (Cardinal) sizeof(TreeInfo));
242
243    tree_info->tree_widget = NULL;
244    tree_info->top_node = NULL;
245    tree_info->active_nodes = NULL;
246    tree_info->num_nodes = tree_info->alloc_nodes = 0;
247    tree_info->flash_widgets = NULL;
248    tree_info->num_flash_widgets = tree_info->alloc_flash_widgets = 0;
249
250    for ( i = 0; i < send_event->num_entries; i++)
251	AddNode(&(tree_info->top_node), (send_event->info + i), tree_info);
252
253    return(tree_info);
254}
255
256/*	Function Name: PrintNodes
257 *	Description: Prints all nodes.
258 *	Arguments: top - the top node.
259 *	Returns: none.
260 */
261
262void
263PrintNodes(WNode *top)
264{
265    Cardinal i;
266
267    if (top->parent == NULL)
268	printf("Top of Tree, Name: %10s, ID: %10ld, Class: %10s\n",
269	       top->name, top->id, top->class);
270    else
271	printf("Parent %10s, Name: %10s, ID: %10ld, Class: %10s\n",
272	       top->parent->name, top->name, top->id, top->class);
273
274    for (i = 0; i < top->num_children; i++)
275	PrintNodes(top->children[i]);
276}
277
278/*	Function Name: _TreeRelabel
279 *	Description: Modifies the selected elements of the tree
280 *	Arguments: tree_info - the tree we are working on.
281 *                 type - type of selection to perform
282 *	Returns: none.
283 */
284
285void
286_TreeRelabel(TreeInfo *tree_info, LabelTypes type)
287{
288    WNode * top;
289
290    if (tree_info == NULL) {
291	SetMessage(global_screen_data.info_label,
292		   res_labels[17]);
293	return;
294    }
295
296    top = tree_info->top_node;
297
298    PrepareToLayoutTree(tree_info->tree_widget);
299    _TreeRelabelNode(top, type, TRUE);
300    LayoutTree(tree_info->tree_widget);
301}
302
303/*	Function Name: _TreeSelect
304 *	Description: Activates relatives of the active nodes, as specified
305 *                   by type, or Selects all nodes as specified by type.
306 *	Arguments: tree_info - information about the tree to work on.
307 *                 type - type of activate to invode.
308 *	Returns: none.
309 */
310
311void
312_TreeSelect(TreeInfo *tree_info, SelectTypes type)
313{
314    WNode ** active_nodes;
315    Cardinal num_active_nodes;
316    Cardinal i;
317
318    if (tree_info == NULL) {
319	SetMessage(global_screen_data.info_label,
320		   res_labels[17]);
321	return;
322    }
323
324    switch(type) {
325    case SelectNone:
326    case SelectAll:
327    case SelectInvert:
328	_TreeSelectNode(tree_info->top_node, type, TRUE);
329	return;
330    default:
331	break;			/* otherwise continue. */
332    }
333
334    if (tree_info->num_nodes == 0) {
335	SetMessage(global_screen_data.info_label,
336		   res_labels[18]);
337	return;
338    }
339
340    active_nodes = CopyActiveNodes(tree_info);
341    num_active_nodes = tree_info->num_nodes;
342
343    for (i = 0; i < num_active_nodes; i++)
344	_TreeActivateNode(active_nodes[i], type);
345
346    XtFree((XtPointer) active_nodes);
347}
348
349/*	Function Name: _TreeSelectNode
350 *	Description: Modifies the state of a node and all its descendants.
351 *	Arguments: node - node to operate on.
352 *                 type - type of selection to perform.
353 *                 recurse - whether to continue on down the tree.
354 *	Returns: none.
355 */
356
357void
358_TreeSelectNode(WNode *node, SelectTypes type, Boolean recurse)
359{
360    Cardinal i;
361    Arg args[1];
362    Boolean state;
363
364    switch(type) {
365    case SelectAll:
366	state = TRUE;
367	break;
368    case SelectNone:
369	state = FALSE;
370	break;
371    case SelectInvert:
372	XtSetArg(args[0], (String)XtNstate, &state);
373	XtGetValues(node->widget, args, ONE);
374
375	state = !state;
376	break;
377    default:
378	SetMessage(global_screen_data.info_label,
379		   res_labels[16]);
380	return;
381    }
382
383    XtSetArg(args[0], (String)XtNstate, state);
384    XtSetValues(node->widget, args, ONE);
385    TreeToggle(node->widget, (XtPointer) node, (XtPointer)(long) state);
386
387    if (!recurse)
388	return;
389
390    for (i = 0; i < node->num_children; i++)
391	_TreeSelectNode(node->children[i], type, recurse);
392}
393
394/*	Function Name: _TreeRelabelNodes
395 *	Description: Modifies the node and all its descendants label.
396 *	Arguments: node - node to operate on.
397 *                 type - type of selection to perform.
398 *                 recurse - whether to continue on down the tree.
399 *	Returns: none.
400 */
401
402void
403_TreeRelabelNode(WNode *node, LabelTypes type, Boolean recurse)
404{
405    Cardinal i;
406    Arg args[1];
407    char buf[30];
408    char *label;
409
410    switch(type) {
411    case ClassLabel:
412	XtSetArg(args[0], XtNlabel, node->class);
413	break;
414    case NameLabel:
415	XtSetArg(args[0], XtNlabel, node->name);
416	break;
417    case IDLabel:
418	snprintf(buf, sizeof(buf), "id: 0x%lx", node->id);
419	XtSetArg(args[0], XtNlabel, buf);
420	break;
421    case WindowLabel:
422	if (node->window == EDITRES_IS_UNREALIZED)
423	    strcpy(buf, "unrealized widget");
424	else if (node->window == EDITRES_IS_OBJECT)
425	    strcpy(buf, "non windowed object");
426	else
427	    snprintf(buf, sizeof(buf), "win: 0x%lx", node->window);
428
429	XtSetArg(args[0], (String)XtNlabel, buf);
430	break;
431    case ToggleLabel:
432	XtSetArg(args[0], (String)XtNlabel, &label);
433	XtGetValues(node->widget, args, ONE);
434	if (label && !strcmp(label, node->name))
435	    XtSetArg(args[0], XtNlabel, node->class);
436	else
437	    XtSetArg(args[0], XtNlabel, node->name);
438	break;
439    default:
440	SetMessage(global_screen_data.info_label,
441		   res_labels[32]);
442	return;
443    }
444
445    XtSetValues(node->widget, args, ONE);
446
447    if (!recurse)
448	return;
449
450    for (i = 0; i < node->num_children; i++)
451	_TreeRelabelNode(node->children[i], type, recurse);
452}
453
454/*	Function Name: _TreeActivateNode
455 *	Description: Activates relatives of the node specified, as specified
456 *                   by type.
457 *	Arguments: node - node to opererate on.
458 *                 type - type of activate to invode.
459 *	Returns: none.
460 */
461
462void
463_TreeActivateNode(WNode* node, SelectTypes type)
464{
465    Arg args[1];
466    Cardinal i;
467
468    XtSetArg(args[0], (String)XtNstate, TRUE);
469
470    if ((type == SelectParent) || (type == SelectAncestors)) {
471	node = node->parent;
472	if (node == NULL)
473	    return;
474
475	XtSetValues(node->widget, args, ONE);
476	AddNodeToActiveList(node);
477
478	if (type == SelectAncestors)
479	    _TreeActivateNode(node, type);
480    }
481    else if ((type == SelectChildren) || (type == SelectDescendants))
482	for (i = 0; i < node->num_children; i++) {
483	    AddNodeToActiveList(node->children[i]);
484	    XtSetValues(node->children[i]->widget, args, ONE);
485	    if (type == SelectDescendants)
486		_TreeActivateNode(node->children[i], type);
487	}
488    else
489	SetMessage(global_screen_data.info_label,
490		   res_labels[33]);
491}
492
493/************************************************************
494 *
495 * Non - Exported Functions.
496 *
497 ************************************************************/
498
499
500/*	Function Name: AddNode
501 *	Description: adds a node to the widget tree.
502 *	Arguments: top_node - a pointer to the current top node.
503 *                 info - the info from the client about the widget tree.
504 *                 tree_info - global information on this tree.
505 *	Returns: none.
506 */
507
508static void
509AddNode(WNode **top_node, WidgetTreeInfo *info, TreeInfo *tree_info)
510{
511    WNode *node, *parent;
512    Boolean early_break = FALSE;
513    Cardinal number = info->widgets.num_widgets;
514
515    if ( (node = FindNode(*top_node, info->widgets.ids, number)) == NULL) {
516	node = (WNode *) XtCalloc(sizeof(WNode), ONE);
517
518	node->id = info->widgets.ids[number - 1];
519	FillNode(info, node, tree_info);
520
521	for ( number--; number > 0; number--, node = parent) {
522	    parent = FindNode(*top_node, info->widgets.ids, number);
523	    if (parent == NULL) {
524		parent = (WNode *) XtCalloc(sizeof(WNode), ONE);
525		parent->id = info->widgets.ids[number - 1];
526	    }
527	    else
528		early_break = TRUE;
529
530	    AddChild(parent, node);
531
532	    if (early_break)
533		break;
534	}
535
536	if (!early_break) {
537	    if (node->parent == NULL)
538		*top_node = node;
539	    else
540		*top_node = node->parent;
541	}
542    }
543    else
544	FillNode(info, node, tree_info);
545}
546
547/*	Function Name: FillNode
548 *	Description: Fills in everything but the node id in the node.
549 *	Arguments: info - the info from the client.
550 *                 node - node to fill.
551 *                 tree_info - global information on this tree.
552 *	Returns: none
553 */
554
555static void
556FillNode(WidgetTreeInfo *info, WNode *node, TreeInfo *tree_info)
557{
558    node->class = info->class;
559    info->class = NULL;	/* keeps it from deallocating. */
560    node->name = info->name;
561    info->name = NULL;
562    node->window = info->window;
563    node->tree_info = tree_info;
564}
565
566/*	Function Name: AddChild
567 *	Description: Adds a child to an existing node.
568 *	Arguments: parent - parent node.
569 *                 child - child node to add.
570 *	Returns: none.
571 */
572
573static void
574AddChild(WNode *parent, WNode *child)
575{
576    if (parent->num_children >= parent->alloc_children) {
577	parent->alloc_children += NUM_INC;
578	parent->children = (WNode **) XtRealloc((char *)parent->children,
579				     sizeof(WNode *) * parent->alloc_children);
580    }
581
582    parent->children[parent->num_children] = child;
583    (parent->num_children)++;
584
585    child->parent = parent;
586}
587
588/************************************************************
589 *
590 *  Functions that operate of the current tree.
591 *
592 ************************************************************/
593
594/*	Function Name: CopyActiveNodes
595 *	Description: returns a copy of the currently selected nodes.
596 *	Arguments: tree_info - the tree info struct.
597 *	Returns: a copy of the selected nodes.
598 */
599
600static WNode **
601CopyActiveNodes(TreeInfo *tree_info)
602{
603    WNode ** list;
604    Cardinal i;
605
606    if ( (tree_info == NULL) || (tree_info->num_nodes == 0))
607	return(NULL);
608
609    list = (WNode **) XtMalloc(sizeof(WNode *) * tree_info->num_nodes);
610
611    for (i = 0; i < tree_info->num_nodes; i++)
612	list[i] = tree_info->active_nodes[i];
613
614    return(list);
615}
616
617/*	Function Name: SetAndCenterTreeNode
618 *	Description: Deactivates all nodes, activates the one specified, and
619 *                   and moves the tree to be centered on the current node.
620 *	Arguments: node - node to use.
621 *	Returns: none.
622 */
623
624void
625SetAndCenterTreeNode(WNode *node)
626{
627    Arg args[5];
628    Cardinal num_args;
629    Position node_x, node_y;
630    Dimension port_width, port_height;
631    Dimension node_width, node_height, node_bw;
632
633    _TreeSelect(node->tree_info, SelectNone); /* Unselect all nodes */
634    _TreeSelectNode(node, SelectAll, FALSE);  /* Select this node */
635
636    /*
637     * Get porthole dimensions.
638     */
639
640    num_args = 0;
641    XtSetArg(args[num_args], XtNwidth, &port_width); num_args++;
642    XtSetArg(args[num_args], XtNheight, &port_height); num_args++;
643    XtGetValues(XtParent(node->tree_info->tree_widget), args, num_args);
644
645    /*
646     * Get node widget dimensions.
647     */
648
649    num_args = 0;
650    XtSetArg(args[num_args], XtNwidth, &node_width); num_args++;
651    XtSetArg(args[num_args], XtNheight, &node_height); num_args++;
652    XtSetArg(args[num_args], XtNborderWidth, &node_bw); num_args++;
653    XtSetArg(args[num_args], XtNx, &node_x); num_args++;
654    XtSetArg(args[num_args], XtNy, &node_y); num_args++;
655    XtGetValues(node->widget, args, num_args);
656
657    /*
658     * reset the node x and y location to be the new x and y location of
659     * the tree relative to the porthole.
660     */
661
662    node_x = port_width/2 - (node_x + node_width/2 + node_bw);
663    node_y = port_height/2 - (node_y + node_height/2 + node_bw);
664
665    num_args = 0;
666    XtSetArg(args[num_args], XtNx, node_x); num_args++;
667    XtSetArg(args[num_args], XtNy, node_y); num_args++;
668    XtSetValues(node->tree_info->tree_widget, args, num_args);
669}
670
671/*	Function Name: PerformTreeToFileDump
672 *	Description: Dumps the contents of the current widget tree to
673 *                   the file specified.
674 *	Arguments: node - node to dump.
675 *                 num_tabs - number of spaces to indent.
676 *                 fp - pointer to the file to write to.
677 *	Returns: none.
678 */
679
680void
681PerformTreeToFileDump(WNode *node, Cardinal num_tabs, FILE *fp)
682{
683    Cardinal i;
684
685    for (i = 0; i < num_tabs; i++)
686	fprintf(fp, "\t");
687    fprintf(fp, "%s  %s\n", node->class, node->name);
688
689    num_tabs++;
690    for (i = 0; i < node->num_children; i++)
691	PerformTreeToFileDump(node->children[i], num_tabs, fp);
692}
693
694