Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  *
      3 Copyright 1989, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in 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 
     42 static void AddNodeToActiveList ( WNode * node );
     43 static void RemoveNodeFromActiveList ( WNode * node );
     44 static Boolean IsActiveNode ( WNode * node );
     45 static void AddNode ( WNode ** top_node, WidgetTreeInfo * info,
     46 		      TreeInfo * tree_info );
     47 static void FillNode ( WidgetTreeInfo * info, WNode * node,
     48 		       TreeInfo * tree_info );
     49 static void AddChild ( WNode * parent, WNode * child );
     50 static 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 */
     60 void
     61 BuildVisualTree(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 
    104 void
    105 AddTreeNode(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 */
    146 void
    147 TreeToggle(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 
    164 static void
    165 AddNodeToActiveList(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 
    188 static void
    189 RemoveNodeFromActiveList(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 
    214 static Boolean
    215 IsActiveNode(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 
    233 TreeInfo *
    234 CreateTree(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 
    262 void
    263 PrintNodes(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 
    285 void
    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 
    311 void
    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 
    357 void
    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 
    402 void
    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 
    462 void
    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 
    508 static void
    509 AddNode(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 
    555 static void
    556 FillNode(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 
    573 static void
    574 AddChild(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 
    600 static WNode **
    601 CopyActiveNodes(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 
    624 void
    625 SetAndCenterTreeNode(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 
    680 void
    681 PerformTreeToFileDump(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