Tree.c revision 7a84e134
1/* 2 * $Xorg: Tree.c,v 1.4 2001/02/09 02:03:47 xorgcvs Exp $ 3 * 4 5Copyright 1990, 1994, 1998 The Open Group 6 7Permission to use, copy, modify, distribute, and sell this software and its 8documentation for any purpose is hereby granted without fee, provided that 9the above copyright notice appear in all copies and that both that 10copyright notice and this permission notice appear in supporting 11documentation. 12 13The above copyright notice and this permission notice shall be included in 14all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall not be 24used in advertising or otherwise to promote the sale, use or other dealings 25in this Software without prior written authorization from The Open Group. 26 27 * Copyright 1989 Prentice Hall 28 * 29 * Permission to use, copy, modify, and distribute this software for any 30 * purpose and without fee is hereby granted, provided that the above 31 * copyright notice appear in all copies and that both the copyright notice 32 * and this permission notice appear in supporting documentation. 33 * 34 * Prentice Hall and the authors disclaim all warranties with regard 35 * to this software, including all implied warranties of merchantability and 36 * fitness. In no event shall Prentice Hall or the authors be liable 37 * for any special, indirect or cosequential damages or any damages whatsoever 38 * resulting from loss of use, data or profits, whether in an action of 39 * contract, negligence or other tortious action, arising out of or in 40 * connection with the use or performance of this software. 41 * 42 * Authors: Jim Fulton, MIT X Consortium, 43 * based on a version by Douglas Young, Prentice Hall 44 * 45 * This widget is based on the Tree widget described on pages 397-419 of 46 * Douglas Young's book "The X Window System, Programming and Applications 47 * with Xt OSF/Motif Edition." The layout code has been rewritten to use 48 * additional blank space to make the structure of the graph easier to see 49 * as well as to support vertical trees. 50 */ 51/* $XFree86: xc/lib/Xaw/Tree.c,v 1.9 2001/01/17 19:42:35 dawes Exp $ */ 52 53#ifdef HAVE_CONFIG_H 54#include <config.h> 55#endif 56#include <X11/IntrinsicP.h> 57#include <X11/StringDefs.h> 58#include <X11/Xaw/XawInit.h> 59#include <X11/Xaw/Cardinals.h> 60#include <X11/Xaw/TreeP.h> 61#include "Private.h" 62 63#define IsHorizontal(tw) ((tw)->tree.gravity == WestGravity || \ 64 (tw)->tree.gravity == EastGravity) 65 66/* 67 * Class Methods 68 */ 69static void XawTreeChangeManaged(Widget); 70static void XawTreeClassInitialize(void); 71static void XawTreeConstraintDestroy(Widget); 72static void XawTreeConstraintInitialize(Widget, Widget, ArgList, Cardinal*); 73static Boolean XawTreeConstraintSetValues(Widget, Widget, Widget, 74 ArgList, Cardinal*); 75static void XawTreeDestroy(Widget); 76static XtGeometryResult XawTreeGeometryManager(Widget, XtWidgetGeometry*, 77 XtWidgetGeometry*); 78static void XawTreeInitialize(Widget, Widget, ArgList, Cardinal*); 79static XtGeometryResult XawTreeQueryGeometry(Widget, XtWidgetGeometry*, 80 XtWidgetGeometry*); 81static void XawTreeRedisplay(Widget, XEvent*, Region); 82static Boolean XawTreeSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 83 84/* 85 * Prototypes 86 */ 87static void arrange_subtree(TreeWidget, Widget, int, int, int); 88static void check_gravity(TreeWidget, XtGravity); 89static void compute_bounding_box_subtree(TreeWidget, Widget, int); 90static void delete_node(Widget, Widget); 91static GC get_tree_gc(TreeWidget); 92static void initialize_dimensions(Dimension**, int*, int); 93static void insert_node(Widget, Widget); 94static void layout_tree(TreeWidget, Bool); 95static void set_positions(TreeWidget, Widget, int); 96static void set_tree_size(TreeWidget, Bool, unsigned int, unsigned int); 97 98/* 99 * Initialization 100 */ 101 102/* 103 * resources of the tree itself 104 */ 105static XtResource resources[] = { 106 { XtNautoReconfigure, XtCAutoReconfigure, XtRBoolean, sizeof (Boolean), 107 XtOffsetOf(TreeRec, tree.auto_reconfigure), XtRImmediate, 108 (XtPointer) FALSE }, 109 { XtNhSpace, XtCHSpace, XtRDimension, sizeof (Dimension), 110 XtOffsetOf(TreeRec, tree.hpad), XtRImmediate, (XtPointer) 0 }, 111 { XtNvSpace, XtCVSpace, XtRDimension, sizeof (Dimension), 112 XtOffsetOf(TreeRec, tree.vpad), XtRImmediate, (XtPointer) 0 }, 113 { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel), 114 XtOffsetOf(TreeRec, tree.foreground), XtRString, 115 XtDefaultForeground}, 116 { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof (Dimension), 117 XtOffsetOf(TreeRec, tree.line_width), XtRImmediate, (XtPointer) 0 }, 118 { XtNgravity, XtCGravity, XtRGravity, sizeof (XtGravity), 119 XtOffsetOf(TreeRec, tree.gravity), XtRImmediate, 120 (XtPointer) WestGravity }, 121#ifndef OLDXAW 122 { XawNdisplayList, XawCDisplayList, XawRDisplayList, sizeof(XawDisplayList*), 123 XtOffsetOf(TreeRec, tree.display_list), XtRImmediate, 124 NULL }, 125#endif 126}; 127 128 129/* 130 * resources that are attached to all children of the tree 131 */ 132static XtResource treeConstraintResources[] = { 133 { XtNtreeParent, XtCTreeParent, XtRWidget, sizeof (Widget), 134 XtOffsetOf(TreeConstraintsRec, tree.parent), XtRImmediate, NULL }, 135 { XtNtreeGC, XtCTreeGC, XtRGC, sizeof(GC), 136 XtOffsetOf(TreeConstraintsRec, tree.gc), XtRImmediate, NULL }, 137}; 138 139 140TreeClassRec treeClassRec = { 141 { 142 /* core_class fields */ 143 (WidgetClass) &constraintClassRec, /* superclass */ 144 "Tree", /* class_name */ 145 sizeof(TreeRec), /* widget_size */ 146 XawTreeClassInitialize, /* class_init */ 147 NULL, /* class_part_init */ 148 FALSE, /* class_inited */ 149 XawTreeInitialize, /* initialize */ 150 NULL, /* initialize_hook */ 151 XtInheritRealize, /* realize */ 152 NULL, /* actions */ 153 0, /* num_actions */ 154 resources, /* resources */ 155 XtNumber(resources), /* num_resources */ 156 NULLQUARK, /* xrm_class */ 157 TRUE, /* compress_motion */ 158 TRUE, /* compress_exposure */ 159 TRUE, /* compress_enterleave*/ 160 TRUE, /* visible_interest */ 161 XawTreeDestroy, /* destroy */ 162 NULL, /* resize */ 163 XawTreeRedisplay, /* expose */ 164 XawTreeSetValues, /* set_values */ 165 NULL, /* set_values_hook */ 166 XtInheritSetValuesAlmost, /* set_values_almost */ 167 NULL, /* get_values_hook */ 168 NULL, /* accept_focus */ 169 XtVersion, /* version */ 170 NULL, /* callback_private */ 171 NULL, /* tm_table */ 172 XawTreeQueryGeometry, /* query_geometry */ 173 NULL, /* display_accelerator*/ 174 NULL, /* extension */ 175 }, 176 { 177 /* composite_class fields */ 178 XawTreeGeometryManager, /* geometry_manager */ 179 XawTreeChangeManaged, /* change_managed */ 180 XtInheritInsertChild, /* insert_child */ 181 XtInheritDeleteChild, /* delete_child */ 182 NULL, /* extension */ 183 }, 184 { 185 /* constraint_class fields */ 186 treeConstraintResources, /* subresources */ 187 XtNumber(treeConstraintResources), /* subresource_count */ 188 sizeof(TreeConstraintsRec), /* constraint_size */ 189 XawTreeConstraintInitialize, /* initialize */ 190 XawTreeConstraintDestroy, /* destroy */ 191 XawTreeConstraintSetValues, /* set_values */ 192 NULL, /* extension */ 193 }, 194 { 195 /* Tree class fields */ 196 0, /* ignore */ 197 } 198}; 199 200WidgetClass treeWidgetClass = (WidgetClass) &treeClassRec; 201 202 203/***************************************************************************** 204 * * 205 * tree utility routines * 206 * * 207 *****************************************************************************/ 208 209static void 210initialize_dimensions(Dimension **listp, int *sizep, int n) 211{ 212 int i; 213 Dimension *l; 214 215 if (!*listp) { 216 *listp = (Dimension *) XtCalloc ((unsigned int) n, 217 (unsigned int) sizeof(Dimension)); 218 *sizep = ((*listp) ? n : 0); 219 return; 220 } 221 if (n > *sizep) { 222 *listp = (Dimension *) XtRealloc((char *) *listp, 223 (unsigned int) (n*sizeof(Dimension))); 224 if (!*listp) { 225 *sizep = 0; 226 return; 227 } 228 for (i = *sizep, l = (*listp) + i; i < n; i++, l++) *l = 0; 229 *sizep = n; 230 } 231 return; 232} 233 234static GC 235get_tree_gc(TreeWidget w) 236{ 237 XtGCMask valuemask = GCBackground | GCForeground; 238 XGCValues values; 239 240 values.background = w->core.background_pixel; 241 values.foreground = w->tree.foreground; 242 if (w->tree.line_width != 0) { 243 valuemask |= GCLineWidth; 244 values.line_width = w->tree.line_width; 245 } 246 247 return XtGetGC ((Widget) w, valuemask, &values); 248} 249 250static void 251insert_node(Widget parent, Widget node) 252{ 253 TreeConstraints pc; 254 TreeConstraints nc = TREE_CONSTRAINT(node); 255 int nindex; 256 257 nc->tree.parent = parent; 258 259 if (parent == NULL) return; 260 261 /* 262 * If there isn't more room in the children array, 263 * allocate additional space. 264 */ 265 pc = TREE_CONSTRAINT(parent); 266 nindex = pc->tree.n_children; 267 268 if (pc->tree.n_children == pc->tree.max_children) { 269 pc->tree.max_children += (pc->tree.max_children / 2) + 2; 270 pc->tree.children = (WidgetList) XtRealloc ((char *)pc->tree.children, 271 (unsigned int) 272 ((pc->tree.max_children) * 273 sizeof(Widget))); 274 } 275 276 /* 277 * Add the sub_node in the next available slot and 278 * increment the counter. 279 */ 280 pc->tree.children[nindex] = node; 281 pc->tree.n_children++; 282} 283 284static void 285delete_node(Widget parent, Widget node) 286{ 287 TreeConstraints pc; 288 int pos, i; 289 290 /* 291 * Make sure the parent exists. 292 */ 293 if (!parent) return; 294 295 pc = TREE_CONSTRAINT(parent); 296 297 /* 298 * Find the sub_node on its parent's list. 299 */ 300 for (pos = 0; pos < pc->tree.n_children; pos++) 301 if (pc->tree.children[pos] == node) break; 302 303 if (pos == pc->tree.n_children) return; 304 305 /* 306 * Decrement the number of children 307 */ 308 pc->tree.n_children--; 309 310 /* 311 * Fill in the gap left by the sub_node. 312 * Zero the last slot for good luck. 313 */ 314 for (i = pos; i < pc->tree.n_children; i++) 315 pc->tree.children[i] = pc->tree.children[i+1]; 316 317 pc->tree.children[pc->tree.n_children]=0; 318} 319 320static void 321check_gravity(TreeWidget tw, XtGravity grav) 322{ 323 switch (tw->tree.gravity) { 324 case WestGravity: case NorthGravity: case EastGravity: case SouthGravity: 325 break; 326 default: 327 tw->tree.gravity = grav; 328 break; 329 } 330} 331 332 333/***************************************************************************** 334 * * 335 * tree class methods * 336 * * 337 *****************************************************************************/ 338 339static void 340XawTreeClassInitialize(void) 341{ 342 XawInitializeWidgetSet(); 343 XtAddConverter(XtRString, XtRGravity, XmuCvtStringToGravity, NULL, 0); 344 XtSetTypeConverter(XtRGravity, XtRString, XmuCvtGravityToString, 345 NULL, 0, XtCacheNone, NULL); 346} 347 348 349/*ARGSUSED*/ 350static void 351XawTreeInitialize(Widget grequest, Widget gnew, 352 ArgList args, Cardinal *num_args) 353{ 354 TreeWidget request = (TreeWidget) grequest, cnew = (TreeWidget) gnew; 355 Arg arglist[2]; 356 357 /* 358 * Make sure the widget's width and height are 359 * greater than zero. 360 */ 361 if (request->core.width <= 0) cnew->core.width = 5; 362 if (request->core.height <= 0) cnew->core.height = 5; 363 364 /* 365 * Set the padding according to the orientation 366 */ 367 if (request->tree.hpad == 0 && request->tree.vpad == 0) { 368 if (IsHorizontal (request)) { 369 cnew->tree.hpad = TREE_HORIZONTAL_DEFAULT_SPACING; 370 cnew->tree.vpad = TREE_VERTICAL_DEFAULT_SPACING; 371 } else { 372 cnew->tree.hpad = TREE_VERTICAL_DEFAULT_SPACING; 373 cnew->tree.vpad = TREE_HORIZONTAL_DEFAULT_SPACING; 374 } 375 } 376 377 /* 378 * Create a graphics context for the connecting lines. 379 */ 380 cnew->tree.gc = get_tree_gc (cnew); 381 382 /* 383 * Create the hidden root widget. 384 */ 385 cnew->tree.tree_root = (Widget) NULL; 386 XtSetArg(arglist[0], XtNwidth, 1); 387 XtSetArg(arglist[1], XtNheight, 1); 388 cnew->tree.tree_root = XtCreateWidget ("root", widgetClass, gnew, 389 arglist,TWO); 390 391 /* 392 * Allocate the array used to hold the widest values per depth 393 */ 394 cnew->tree.largest = NULL; 395 cnew->tree.n_largest = 0; 396 initialize_dimensions (&cnew->tree.largest, &cnew->tree.n_largest, 397 TREE_INITIAL_DEPTH); 398 399 /* 400 * make sure that our gravity is one of the acceptable values 401 */ 402 check_gravity (cnew, WestGravity); 403} 404 405 406/* ARGSUSED */ 407static void 408XawTreeConstraintInitialize(Widget request, Widget cnew, 409 ArgList args, Cardinal *num_args) 410{ 411 TreeConstraints tc = TREE_CONSTRAINT(cnew); 412 TreeWidget tw = (TreeWidget) cnew->core.parent; 413 414 /* 415 * Initialize the widget to have no sub-nodes. 416 */ 417 tc->tree.n_children = 0; 418 tc->tree.max_children = 0; 419 tc->tree.children = (Widget *) NULL; 420 tc->tree.x = tc->tree.y = 0; 421 tc->tree.bbsubwidth = 0; 422 tc->tree.bbsubheight = 0; 423 424 425 /* 426 * If this widget has a super-node, add it to that 427 * widget' sub-nodes list. Otherwise make it a sub-node of 428 * the tree_root widget. 429 */ 430 if (tc->tree.parent) 431 insert_node (tc->tree.parent, cnew); 432 else if (tw->tree.tree_root) 433 insert_node (tw->tree.tree_root, cnew); 434} 435 436 437/* ARGSUSED */ 438static Boolean 439XawTreeSetValues(Widget gcurrent, Widget grequest, Widget gnew, 440 ArgList args, Cardinal *num_args) 441{ 442 TreeWidget current = (TreeWidget) gcurrent, cnew = (TreeWidget) gnew; 443 Boolean redraw = FALSE; 444 445 /* 446 * If the foreground color has changed, redo the GC's 447 * and indicate a redraw. 448 */ 449 if (cnew->tree.foreground != current->tree.foreground || 450 cnew->core.background_pixel != current->core.background_pixel || 451 cnew->tree.line_width != current->tree.line_width) { 452 XtReleaseGC (gnew, cnew->tree.gc); 453 cnew->tree.gc = get_tree_gc (cnew); 454 redraw = TRUE; 455 } 456 457 /* 458 * If the minimum spacing has changed, recalculate the 459 * tree layout. layout_tree() does a redraw, so we don't 460 * need SetValues to do another one. 461 */ 462 if (cnew->tree.gravity != current->tree.gravity) { 463 check_gravity (cnew, current->tree.gravity); 464 } 465 466 if (IsHorizontal(cnew) != IsHorizontal(current)) { 467 if (cnew->tree.vpad == current->tree.vpad && 468 cnew->tree.hpad == current->tree.hpad) { 469 cnew->tree.vpad = current->tree.hpad; 470 cnew->tree.hpad = current->tree.vpad; 471 } 472 } 473 474 if (cnew->tree.vpad != current->tree.vpad || 475 cnew->tree.hpad != current->tree.hpad || 476 cnew->tree.gravity != current->tree.gravity) { 477 layout_tree (cnew, TRUE); 478 redraw = FALSE; 479 } 480 return redraw; 481} 482 483 484/* ARGSUSED */ 485static Boolean 486XawTreeConstraintSetValues(Widget current, Widget request, Widget cnew, 487 ArgList args, Cardinal *num_args) 488{ 489 TreeConstraints newc = TREE_CONSTRAINT(cnew); 490 TreeConstraints curc = TREE_CONSTRAINT(current); 491 TreeWidget tw = (TreeWidget) cnew->core.parent; 492 493 /* 494 * If the parent field has changed, remove the widget 495 * from the old widget's children list and add it to the 496 * new one. 497 */ 498 if (curc->tree.parent != newc->tree.parent){ 499 if (curc->tree.parent) 500 delete_node (curc->tree.parent, cnew); 501 if (newc->tree.parent) 502 insert_node(newc->tree.parent, cnew); 503 504 /* 505 * If the Tree widget has been realized, 506 * compute new layout. 507 */ 508 if (XtIsRealized((Widget)tw)) 509 layout_tree (tw, FALSE); 510 } 511 return False; 512} 513 514 515static void 516XawTreeConstraintDestroy(Widget w) 517{ 518 TreeConstraints tc = TREE_CONSTRAINT(w); 519 TreeWidget tw = (TreeWidget) XtParent(w); 520 int i; 521 522 /* 523 * Remove the widget from its parent's sub-nodes list and 524 * make all this widget's sub-nodes sub-nodes of the parent. 525 */ 526 527 if (tw->tree.tree_root == w) { 528 if (tc->tree.n_children > 0) 529 tw->tree.tree_root = tc->tree.children[0]; 530 else 531 tw->tree.tree_root = NULL; 532 } 533 534 delete_node (tc->tree.parent, (Widget) w); 535 for (i = 0; i< tc->tree.n_children; i++) 536 insert_node (tc->tree.parent, tc->tree.children[i]); 537 538 layout_tree ((TreeWidget) (w->core.parent), FALSE); 539} 540 541/* ARGSUSED */ 542static XtGeometryResult 543XawTreeGeometryManager(Widget w, XtWidgetGeometry *request, 544 XtWidgetGeometry *reply) 545{ 546 547 TreeWidget tw = (TreeWidget) w->core.parent; 548 549 /* 550 * No position changes allowed!. 551 */ 552 if ((request->request_mode & CWX && request->x!=w->core.x) 553 ||(request->request_mode & CWY && request->y!=w->core.y)) 554 return (XtGeometryNo); 555 556 /* 557 * Allow all resize requests. 558 */ 559 560 if (request->request_mode & CWWidth) 561 w->core.width = request->width; 562 if (request->request_mode & CWHeight) 563 w->core.height = request->height; 564 if (request->request_mode & CWBorderWidth) 565 w->core.border_width = request->border_width; 566 567 if (tw->tree.auto_reconfigure) layout_tree (tw, FALSE); 568 return (XtGeometryYes); 569} 570 571static void 572XawTreeChangeManaged(Widget gw) 573{ 574 layout_tree ((TreeWidget) gw, FALSE); 575} 576 577 578static void 579XawTreeDestroy(Widget gw) 580{ 581 TreeWidget w = (TreeWidget) gw; 582 583 XtReleaseGC (gw, w->tree.gc); 584 if (w->tree.largest) XtFree ((char *) w->tree.largest); 585} 586 587 588/* ARGSUSED */ 589static void 590XawTreeRedisplay(Widget gw, XEvent *event, Region region) 591{ 592 TreeWidget tw = (TreeWidget) gw; 593 594#ifndef OLDXAW 595 if (tw->tree.display_list) 596 XawRunDisplayList(gw, tw->tree.display_list, event, region); 597#endif 598 599 /* 600 * If the Tree widget is visible, visit each managed child. 601 */ 602 if (tw->core.visible) { 603 Cardinal i; 604 int j; 605 Display *dpy = XtDisplay (tw); 606 Window w = XtWindow (tw); 607 608 for (i = 0; i < tw->composite.num_children; i++) { 609 Widget child = tw->composite.children[i]; 610 TreeConstraints tc = TREE_CONSTRAINT(child); 611 612 /* 613 * Don't draw lines from the fake tree_root. 614 */ 615 if (child != tw->tree.tree_root && tc->tree.n_children) { 616 int srcx = child->core.x + child->core.border_width; 617 int srcy = child->core.y + child->core.border_width; 618 619 switch (tw->tree.gravity) { 620 case WestGravity: 621 srcx += child->core.width + child->core.border_width; 622 /* fall through */ 623 case EastGravity: 624 srcy += child->core.height / 2; 625 break; 626 627 case NorthGravity: 628 srcy += child->core.height + child->core.border_width; 629 /* fall through */ 630 case SouthGravity: 631 srcx += child->core.width / 2; 632 break; 633 } 634 635 for (j = 0; j < tc->tree.n_children; j++) { 636 Widget k = tc->tree.children[j]; 637 GC gc = (tc->tree.gc ? tc->tree.gc : tw->tree.gc); 638 639 switch (tw->tree.gravity) { 640 case WestGravity: 641 /* 642 * right center to left center 643 */ 644 XDrawLine (dpy, w, gc, srcx, srcy, 645 (int) k->core.x, 646 (k->core.y + ((int) k->core.border_width) + 647 ((int) k->core.height) / 2)); 648 break; 649 650 case NorthGravity: 651 /* 652 * bottom center to top center 653 */ 654 XDrawLine (dpy, w, gc, srcx, srcy, 655 (k->core.x + ((int) k->core.border_width) + 656 ((int) k->core.width) / 2), 657 (int) k->core.y); 658 break; 659 660 case EastGravity: 661 /* 662 * left center to right center 663 */ 664 XDrawLine (dpy, w, gc, srcx, srcy, 665 (k->core.x + 666 (((int) k->core.border_width) << 1) + 667 (int) k->core.width), 668 (k->core.y + ((int) k->core.border_width) + 669 ((int) k->core.height) / 2)); 670 break; 671 672 case SouthGravity: 673 /* 674 * top center to bottom center 675 */ 676 XDrawLine (dpy, w, gc, srcx, srcy, 677 (k->core.x + ((int) k->core.border_width) + 678 ((int) k->core.width) / 2), 679 (k->core.y + 680 (((int) k->core.border_width) << 1) + 681 (int) k->core.height)); 682 break; 683 } 684 } 685 } 686 } 687 } 688} 689 690static XtGeometryResult 691XawTreeQueryGeometry(Widget w, XtWidgetGeometry *intended, 692 XtWidgetGeometry *preferred) 693{ 694 TreeWidget tw = (TreeWidget) w; 695 696 preferred->request_mode = (CWWidth | CWHeight); 697 preferred->width = tw->tree.maxwidth; 698 preferred->height = tw->tree.maxheight; 699 700 if (((intended->request_mode & (CWWidth | CWHeight)) == 701 (CWWidth | CWHeight)) && 702 intended->width == preferred->width && 703 intended->height == preferred->height) 704 return XtGeometryYes; 705 else if (preferred->width == w->core.width && 706 preferred->height == w->core.height) 707 return XtGeometryNo; 708 else 709 return XtGeometryAlmost; 710} 711 712 713/***************************************************************************** 714 * * 715 * tree layout algorithm * 716 * * 717 * Each node in the tree is "shrink-wrapped" with a minimal bounding * 718 * rectangle, laid next to its siblings (with a small about of padding in * 719 * between) and then wrapped with their parent. Parents are centered about * 720 * their children (or vice versa if the parent is larger than the children). * 721 * * 722 *****************************************************************************/ 723 724static void 725compute_bounding_box_subtree(TreeWidget tree, Widget w, int depth) 726{ 727 TreeConstraints tc = TREE_CONSTRAINT(w); /* info attached to all kids */ 728 int i; 729 Bool horiz = IsHorizontal (tree); 730 Dimension newwidth, newheight; 731 Dimension bw2 = w->core.border_width * 2; 732 733 /* 734 * Set the max-size per level. 735 */ 736 if (depth >= tree->tree.n_largest) { 737 initialize_dimensions (&tree->tree.largest, 738 &tree->tree.n_largest, depth + 1); 739 } 740 newwidth = ((horiz ? w->core.width : w->core.height) + bw2); 741 if (tree->tree.largest[depth] < newwidth) 742 tree->tree.largest[depth] = newwidth; 743 744 745 /* 746 * initialize 747 */ 748 tc->tree.bbwidth = w->core.width + bw2; 749 tc->tree.bbheight = w->core.height + bw2; 750 751 if (tc->tree.n_children == 0) return; 752 753 /* 754 * Figure the size of the opposite dimension (vertical if tree is 755 * horizontal, else vice versa). The other dimension will be set 756 * in the second pass once we know the maximum dimensions. 757 */ 758 newwidth = 0; 759 newheight = 0; 760 for (i = 0; i < tc->tree.n_children; i++) { 761 Widget child = tc->tree.children[i]; 762 TreeConstraints cc = TREE_CONSTRAINT(child); 763 764 compute_bounding_box_subtree (tree, child, depth + 1); 765 766 if (horiz) { 767 if (newwidth < cc->tree.bbwidth) newwidth = cc->tree.bbwidth; 768 newheight += tree->tree.vpad + cc->tree.bbheight; 769 } else { 770 if (newheight < cc->tree.bbheight) newheight = cc->tree.bbheight; 771 newwidth += tree->tree.hpad + cc->tree.bbwidth; 772 } 773 } 774 775 776 tc->tree.bbsubwidth = newwidth; 777 tc->tree.bbsubheight = newheight; 778 779 /* 780 * Now fit parent onto side (or top) of bounding box and correct for 781 * extra padding. Be careful of unsigned arithmetic. 782 */ 783 if (horiz) { 784 tc->tree.bbwidth += tree->tree.hpad + newwidth; 785 newheight -= tree->tree.vpad; 786 if (newheight > tc->tree.bbheight) tc->tree.bbheight = newheight; 787 } else { 788 tc->tree.bbheight += tree->tree.vpad + newheight; 789 newwidth -= tree->tree.hpad; 790 if (newwidth > tc->tree.bbwidth) tc->tree.bbwidth = newwidth; 791 } 792} 793 794 795static void 796set_positions(TreeWidget tw, Widget w, int level) 797{ 798 int i; 799 800 if (w) { 801 TreeConstraints tc = TREE_CONSTRAINT(w); 802 803 if (level > 0) { 804 /* 805 * mirror if necessary 806 */ 807 switch (tw->tree.gravity) { 808 case EastGravity: 809 tc->tree.x = (((Position) tw->tree.maxwidth) - 810 ((Position) w->core.width) - tc->tree.x); 811 break; 812 813 case SouthGravity: 814 tc->tree.y = (((Position) tw->tree.maxheight) - 815 ((Position) w->core.height) - tc->tree.y); 816 break; 817 } 818 819 /* 820 * Move the widget into position. 821 */ 822 XtMoveWidget (w, tc->tree.x, tc->tree.y); 823 } 824 825 /* 826 * Set the positions of all children. 827 */ 828 for (i = 0; i < tc->tree.n_children; i++) 829 set_positions (tw, tc->tree.children[i], level + 1); 830 } 831} 832 833 834static void 835arrange_subtree(TreeWidget tree, Widget w, int depth, int x, int y) 836{ 837 TreeConstraints tc = TREE_CONSTRAINT(w); /* info attached to all kids */ 838 TreeConstraints firstcc, lastcc; 839 int i; 840 int newx, newy; 841 Bool horiz = IsHorizontal (tree); 842 Widget child = NULL; 843 Dimension tmp; 844 Dimension bw2 = w->core.border_width * 2; 845 Bool relayout = True; 846 847 848 /* 849 * If no children, then just lay out where requested. 850 */ 851 tc->tree.x = x; 852 tc->tree.y = y; 853 854 if (horiz) { 855 int myh = (w->core.height + bw2); 856 857 if (myh > (int)tc->tree.bbsubheight) { 858 y += (myh - (int)tc->tree.bbsubheight) / 2; 859 relayout = False; 860 } 861 } else { 862 int myw = (w->core.width + bw2); 863 864 if (myw > (int)tc->tree.bbsubwidth) { 865 x += (myw - (int)tc->tree.bbsubwidth) / 2; 866 relayout = False; 867 } 868 } 869 870 if ((tmp = ((Dimension) x) + tc->tree.bbwidth) > tree->tree.maxwidth) 871 tree->tree.maxwidth = tmp; 872 if ((tmp = ((Dimension) y) + tc->tree.bbheight) > tree->tree.maxheight) 873 tree->tree.maxheight = tmp; 874 875 if (tc->tree.n_children == 0) return; 876 877 878 /* 879 * Have children, so walk down tree laying out children, then laying 880 * out parents. 881 */ 882 if (horiz) { 883 newx = x + tree->tree.largest[depth]; 884 if (depth > 0) newx += tree->tree.hpad; 885 newy = y; 886 } else { 887 newx = x; 888 newy = y + tree->tree.largest[depth]; 889 if (depth > 0) newy += tree->tree.vpad; 890 } 891 892 for (i = 0; i < tc->tree.n_children; i++) { 893 TreeConstraints cc; 894 895 child = tc->tree.children[i]; /* last value is used outside loop */ 896 cc = TREE_CONSTRAINT(child); 897 898 arrange_subtree (tree, child, depth + 1, newx, newy); 899 if (horiz) { 900 newy += tree->tree.vpad + cc->tree.bbheight; 901 } else { 902 newx += tree->tree.hpad + cc->tree.bbwidth; 903 } 904 } 905 906 /* 907 * now layout parent between first and last children 908 */ 909 if (relayout) { 910 Position adjusted; 911 firstcc = TREE_CONSTRAINT (tc->tree.children[0]); 912 lastcc = TREE_CONSTRAINT (child); 913 914 /* Adjustments are disallowed if they result in a position above 915 * or to the left of the originally requested position, because 916 * this could collide with the position of the previous sibling. 917 */ 918 if (horiz) { 919 tc->tree.x = x; 920 adjusted = firstcc->tree.y + 921 ((lastcc->tree.y + (Position) child->core.height + 922 (Position) child->core.border_width * 2 - 923 firstcc->tree.y - (Position) w->core.height - 924 (Position) w->core.border_width * 2 + 1) / 2); 925 if (adjusted > tc->tree.y) tc->tree.y = adjusted; 926 } else { 927 adjusted = firstcc->tree.x + 928 ((lastcc->tree.x + (Position) child->core.width + 929 (Position) child->core.border_width * 2 - 930 firstcc->tree.x - (Position) w->core.width - 931 (Position) w->core.border_width * 2 + 1) / 2); 932 if (adjusted > tc->tree.x) tc->tree.x = adjusted; 933 tc->tree.y = y; 934 } 935 } 936} 937 938static void 939set_tree_size(TreeWidget tw, Bool insetvalues, 940 unsigned int width, unsigned int height) 941{ 942 if (insetvalues) { 943 tw->core.width = width; 944 tw->core.height = height; 945 } else { 946 Dimension replyWidth = 0, replyHeight = 0; 947 XtGeometryResult result = XtMakeResizeRequest ((Widget) tw, 948 width, height, 949 &replyWidth, 950 &replyHeight); 951 /* 952 * Accept any compromise. 953 */ 954 if (result == XtGeometryAlmost) 955 XtMakeResizeRequest ((Widget) tw, replyWidth, replyHeight, 956 (Dimension *) NULL, (Dimension *) NULL); 957 } 958 return; 959} 960 961static void 962layout_tree(TreeWidget tw, Bool insetvalues) 963{ 964 int i; 965 Dimension *dp; 966 967 /* 968 * Do a depth-first search computing the width and height of the bounding 969 * box for the tree at that position (and below). Then, walk again using 970 * this information to layout the children at each level. 971 */ 972 973 if (tw->tree.tree_root == NULL) 974 return; 975 976 tw->tree.maxwidth = tw->tree.maxheight = 0; 977 for (i = 0, dp = tw->tree.largest; i < tw->tree.n_largest; i++, dp++) 978 *dp = 0; 979 initialize_dimensions (&tw->tree.largest, &tw->tree.n_largest, 980 tw->tree.n_largest); 981 compute_bounding_box_subtree (tw, tw->tree.tree_root, 0); 982 983 /* 984 * Second pass to do final layout. Each child's bounding box is stacked 985 * on top of (if horizontal, else next to) on top of its siblings. The 986 * parent is centered between the first and last children. 987 */ 988 arrange_subtree (tw, tw->tree.tree_root, 0, 0, 0); 989 990 /* 991 * Move each widget into place. 992 */ 993 set_tree_size (tw, insetvalues, tw->tree.maxwidth, tw->tree.maxheight); 994 set_positions (tw, tw->tree.tree_root, 0); 995 996 /* 997 * And redisplay. 998 */ 999 if (XtIsRealized ((Widget) tw)) { 1000 XClearArea (XtDisplay(tw), XtWindow((Widget)tw), 0, 0, 0, 0, True); 1001 } 1002} 1003 1004 1005 1006/***************************************************************************** 1007 * * 1008 * Public Routines * 1009 * * 1010 *****************************************************************************/ 1011 1012void 1013XawTreeForceLayout(Widget tree) 1014{ 1015 layout_tree ((TreeWidget) tree, FALSE); 1016} 1017 1018