Paned.c revision 7a84e134
1/* $Xorg: Paned.c,v 1.4 2001/02/09 02:03:45 xorgcvs Exp $ */ 2 3/*********************************************************** 4 5Copyright 1987, 1988, 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 28Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. 29 30 All Rights Reserved 31 32Permission to use, copy, modify, and distribute this software and its 33documentation for any purpose and without fee is hereby granted, 34provided that the above copyright notice appear in all copies and that 35both that copyright notice and this permission notice appear in 36supporting documentation, and that the name of Digital not be 37used in advertising or publicity pertaining to distribution of the 38software without specific, written prior permission. 39 40DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 41ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 42DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 43ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 44WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 45ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 46SOFTWARE. 47 48******************************************************************/ 49/* $XFree86: xc/lib/Xaw/Paned.c,v 1.11 2001/08/23 00:03:19 dawes Exp $ */ 50 51/* 52 * Updated and significantly modified from the Athena VPaned Widget. 53 * 54 * Date: March 1, 1989 55 * 56 * By: Chris D. Peterson 57 * MIT X Consortium 58 * kit@expo.lcs.mit.edu 59 */ 60 61#ifdef HAVE_CONFIG_H 62#include <config.h> 63#endif 64#include <X11/IntrinsicP.h> 65#include <X11/cursorfont.h> 66#include <X11/StringDefs.h> 67#include <X11/Xmu/CharSet.h> 68#include <X11/Xmu/Converters.h> 69#include <X11/Xmu/Misc.h> 70#include <X11/Xmu/SysUtil.h> 71#include <X11/Xaw/Grip.h> 72#include <X11/Xaw/PanedP.h> 73#include <X11/Xaw/XawImP.h> 74#include <X11/Xaw/XawInit.h> 75#include "Private.h" 76 77typedef enum { 78 UpLeftPane = 'U', 79 LowRightPane = 'L', 80 ThisBorderOnly = 'T', 81 AnyPane = 'A' 82} Direction; 83 84#define NO_INDEX -100 85#define IS_GRIP NULL 86 87#define PaneInfo(w) ((Pane)(w)->core.constraints) 88#define HasGrip(w) (PaneInfo(w)->grip != NULL) 89#define IsPane(w) ((w)->core.widget_class != gripWidgetClass) 90#define PaneIndex(w) (PaneInfo(w)->position) 91#define IsVert(w) ((w)->paned.orientation == XtorientVertical) 92 93#define ForAllPanes(pw, childP) \ 94for ((childP) = (pw)->composite.children; \ 95 (childP) < (pw)->composite.children + (pw)->paned.num_panes; \ 96 (childP)++) 97 98#define ForAllChildren(pw, childP) \ 99for ((childP) = (pw)->composite.children; \ 100 (childP) < (pw)->composite.children + (pw)->composite.num_children; \ 101 (childP)++) 102 103#define PaneSize(paned, vertical) \ 104 ((vertical) ? XtHeight(paned) : XtWidth(paned)) 105 106#define GetRequestInfo(geo, vertical) \ 107 ((vertical) ? (geo)->height : (geo)->width) 108 109#define SatisfiesRule1(pane, shrink) \ 110 (((shrink) && ((pane)->size != (pane)->min)) \ 111 || (!(shrink) && ((pane)->size != (pane)->max))) 112 113#define SatisfiesRule2(pane) \ 114 (!(pane)->skip_adjust || (pane)->paned_adjusted_me) 115 116#define SatisfiesRule3(pane, shrink) \ 117 ((pane)->paned_adjusted_me \ 118 && (((shrink) && ((int)(pane)->wp_size <= (pane)->size)) \ 119 || (!(shrink) && ((int)(pane)->wp_size >= (pane)->size)))) 120 121 122/* 123 * Class Methods 124 */ 125static void XawPanedClassInitialize(void); 126static void XawPanedChangeManaged(Widget); 127static void XawPanedDeleteChild(Widget); 128static void XawPanedDestroy(Widget); 129static XtGeometryResult XawPanedGeometryManager(Widget, XtWidgetGeometry*, 130 XtWidgetGeometry*); 131static void XawPanedInitialize(Widget, Widget, ArgList, Cardinal*); 132static void XawPanedInsertChild(Widget); 133static Boolean XawPanedPaneSetValues(Widget, Widget, Widget, 134 ArgList, Cardinal*); 135static void XawPanedRealize(Widget, Mask*, XSetWindowAttributes*); 136static void XawPanedRedisplay(Widget, XEvent*, Region); 137static void XawPanedResize(Widget); 138static Boolean XawPanedSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 139 140/* 141 * Prototypes 142 */ 143static void _DrawInternalBorders(PanedWidget, GC); 144static void _DrawRect(PanedWidget, GC, int, int, unsigned int, unsigned int); 145static void _DrawTrackLines(PanedWidget, Bool); 146static void AdjustPanedSize(PanedWidget, unsigned int, XtGeometryResult*, 147 Dimension*, Dimension*); 148static void ChangeAllGripCursors(PanedWidget); 149static Pane ChoosePaneToResize(PanedWidget, int, Direction, Bool); 150static void ClearPaneStack(PanedWidget); 151static void CommitGripAdjustment(PanedWidget); 152static void CreateGrip(Widget); 153static int GetEventLocation(PanedWidget, XEvent*); 154static void GetGCs(Widget); 155static void GetPaneStack(PanedWidget, Bool, Pane*, int*); 156static void HandleGrip(Widget, XtPointer, XtPointer); 157static void LoopAndRefigureChildren(PanedWidget, int, Direction, int*); 158static void ManageAndUnmanageGrips(PanedWidget); 159static void MoveGripAdjustment(PanedWidget, Widget, Direction, int); 160static Bool PopPaneStack(PanedWidget); 161static void PushPaneStack(PanedWidget, Pane); 162static void RefigureLocations(PanedWidget, int, Direction); 163static void RefigureLocationsAndCommit(Widget); 164static void ReleaseGCs(Widget); 165static void ResortChildren(PanedWidget); 166static void SetChildrenPrefSizes(PanedWidget, unsigned int); 167static void StartGripAdjustment(PanedWidget, Widget, Direction); 168 169/* 170 * Initialization 171 */ 172static char defGripTranslations[] = 173"<Btn1Down>:" "GripAction(Start,UpLeftPane)\n" 174"<Btn2Down>:" "GripAction(Start,ThisBorderOnly)\n" 175"<Btn3Down>:" "GripAction(Start,LowRightPane)\n" 176"<Btn1Motion>:" "GripAction(Move,UpLeft)\n" 177"<Btn2Motion>:" "GripAction(Move,ThisBorder)\n" 178"<Btn3Motion>:" "GripAction(Move,LowRight)\n" 179"Any<BtnUp>:" "GripAction(Commit)\n" 180; 181 182#define offset(field) XtOffsetOf(PanedRec, paned.field) 183static XtResource resources[] = { 184 { 185 XtNinternalBorderColor, 186 XtCBorderColor, 187 XtRPixel, 188 sizeof(Pixel), 189 offset(internal_bp), 190 XtRString, 191 (XtPointer)XtDefaultForeground 192 }, 193 { 194 XtNinternalBorderWidth, 195 XtCBorderWidth, 196 XtRDimension, 197 sizeof(Dimension), 198 offset(internal_bw), 199 XtRImmediate, 200 (XtPointer)1 201 }, 202 { 203 XtNgripIndent, 204 XtCGripIndent, 205 XtRPosition, 206 sizeof(Position), 207 offset(grip_indent), 208 XtRImmediate, 209 (XtPointer)10 210 }, 211 { 212 XtNrefigureMode, 213 XtCBoolean, 214 XtRBoolean, 215 sizeof(Boolean), 216 offset(refiguremode), 217 XtRImmediate, 218 (XtPointer)True 219 }, 220 { 221 XtNgripTranslations, 222 XtCTranslations, 223 XtRTranslationTable, 224 sizeof(XtTranslations), 225 offset(grip_translations), 226 XtRString, 227 (XtPointer)defGripTranslations 228 }, 229 { 230 XtNorientation, 231 XtCOrientation, 232 XtROrientation, 233 sizeof(XtOrientation), 234 offset(orientation), 235 XtRImmediate, 236 (XtPointer)XtorientVertical 237 }, 238 { 239 XtNcursor, 240 XtCCursor, 241 XtRCursor, 242 sizeof(Cursor), 243 offset(cursor), 244 XtRImmediate, 245 None 246 }, 247 { 248 XtNgripCursor, 249 XtCCursor, 250 XtRCursor, 251 sizeof(Cursor), 252 offset(grip_cursor), 253 XtRImmediate, 254 None 255 }, 256 { 257 XtNverticalGripCursor, 258 XtCCursor, 259 XtRCursor, 260 sizeof(Cursor), 261 offset(v_grip_cursor), 262 XtRString, 263 "sb_v_double_arrow" 264 }, 265 { 266 XtNhorizontalGripCursor, 267 XtCCursor, 268 XtRCursor, 269 sizeof(Cursor), 270 offset(h_grip_cursor), 271 XtRString, 272 "sb_h_double_arrow" 273 }, 274 { 275 XtNbetweenCursor, 276 XtCCursor, 277 XtRCursor, 278 sizeof(Cursor), 279 offset(adjust_this_cursor), 280 XtRString, 281 None 282 }, 283 { 284 XtNverticalBetweenCursor, 285 XtCCursor, 286 XtRCursor, 287 sizeof(Cursor), 288 offset(v_adjust_this_cursor), 289 XtRString, 290 "sb_left_arrow" 291 }, 292 { 293 XtNhorizontalBetweenCursor, 294 XtCCursor, 295 XtRCursor, 296 sizeof(Cursor), 297 offset(h_adjust_this_cursor), 298 XtRString, 299 "sb_up_arrow" 300 }, 301 { 302 XtNupperCursor, 303 XtCCursor, 304 XtRCursor, 305 sizeof(Cursor), 306 offset(adjust_upper_cursor), 307 XtRString, 308 "sb_up_arrow" 309 }, 310 { 311 XtNlowerCursor, 312 XtCCursor, 313 XtRCursor, 314 sizeof(Cursor), 315 offset(adjust_lower_cursor), 316 XtRString, 317 "sb_down_arrow" 318 }, 319 { 320 XtNleftCursor, 321 XtCCursor, 322 XtRCursor, 323 sizeof(Cursor), 324 offset(adjust_left_cursor), 325 XtRString, 326 "sb_left_arrow" 327 }, 328 { 329 XtNrightCursor, 330 XtCCursor, 331 XtRCursor, 332 sizeof(Cursor), 333 offset(adjust_right_cursor), 334 XtRString, 335 "sb_right_arrow" 336 }, 337}; 338#undef offset 339 340#define offset(field) XtOffsetOf(PanedConstraintsRec, paned.field) 341static XtResource subresources[] = { 342 { 343 XtNallowResize, 344 XtCBoolean, 345 XtRBoolean, 346 sizeof(Boolean), 347 offset(allow_resize), 348 XtRImmediate, 349 (XtPointer)False 350 }, 351 { 352 XtNposition, 353 XtCPosition, 354 XtRInt, 355 sizeof(int), 356 offset(position), 357 XtRImmediate, 358 (XtPointer)0 359 }, 360 { 361 XtNmin, 362 XtCMin, 363 XtRDimension, 364 sizeof(Dimension), 365 offset(min), 366 XtRImmediate, 367 (XtPointer)PANED_GRIP_SIZE 368 }, 369 { 370 XtNmax, 371 XtCMax, 372 XtRDimension, 373 sizeof(Dimension), 374 offset(max), 375 XtRImmediate, 376 (XtPointer)~0 377 }, 378 { 379 XtNpreferredPaneSize, 380 XtCPreferredPaneSize, 381 XtRDimension, 382 sizeof(Dimension), 383 offset(preferred_size), 384 XtRImmediate, 385 (XtPointer)PANED_ASK_CHILD 386 }, 387 { 388 XtNresizeToPreferred, 389 XtCBoolean, 390 XtRBoolean, 391 sizeof(Boolean), 392 offset(resize_to_pref), 393 XtRImmediate, 394 (XtPointer)False 395 }, 396 { 397 XtNskipAdjust, 398 XtCBoolean, 399 XtRBoolean, 400 sizeof(Boolean), 401 offset(skip_adjust), 402 XtRImmediate, 403 (XtPointer)False 404 }, 405 { 406 XtNshowGrip, 407 XtCShowGrip, 408 XtRBoolean, 409 sizeof(Boolean), 410 offset(show_grip), 411 XtRImmediate, 412 (XtPointer)True 413 }, 414}; 415#undef offset 416 417#define SuperClass ((ConstraintWidgetClass)&constraintClassRec) 418 419PanedClassRec panedClassRec = { 420 /* core */ 421 { 422 (WidgetClass)SuperClass, /* superclass */ 423 "Paned", /* class name */ 424 sizeof(PanedRec), /* size */ 425 XawPanedClassInitialize, /* class_initialize */ 426 NULL, /* class_part init */ 427 False, /* class_inited */ 428 XawPanedInitialize, /* initialize */ 429 NULL, /* initialize_hook */ 430 XawPanedRealize, /* realize */ 431 NULL, /* actions */ 432 0, /* num_actions */ 433 resources, /* resources */ 434 XtNumber(resources), /* num_resources */ 435 NULLQUARK, /* xrm_class */ 436 True, /* compress_motion */ 437 True, /* compress_exposure */ 438 True, /* compress_enterleave */ 439 False, /* visible_interest */ 440 XawPanedDestroy, /* destroy */ 441 XawPanedResize, /* resize */ 442 XawPanedRedisplay, /* expose */ 443 XawPanedSetValues, /* set_values */ 444 NULL, /* set_values_hook */ 445 XtInheritSetValuesAlmost, /* set_values_almost */ 446 NULL, /* get_values_hook */ 447 NULL, /* accept_focus */ 448 XtVersion, /* version */ 449 NULL, /* callback_private */ 450 NULL, /* tm_table */ 451 XtInheritQueryGeometry, /* query_geometry */ 452 XtInheritDisplayAccelerator, /* display_accelerator */ 453 NULL, /* extension */ 454 }, 455 /* composite */ 456 { 457 XawPanedGeometryManager, /* geometry_manager */ 458 XawPanedChangeManaged, /* change_managed */ 459 XawPanedInsertChild, /* insert_child */ 460 XawPanedDeleteChild, /* delete_child */ 461 NULL, /* extension */ 462 }, 463 /* constraint */ 464 { 465 subresources, /* subresources */ 466 XtNumber(subresources), /* subresource_count */ 467 sizeof(PanedConstraintsRec), /* constraint_size */ 468 NULL, /* initialize */ 469 NULL, /* destroy */ 470 XawPanedPaneSetValues, /* set_values */ 471 NULL, /* extension */ 472 }, 473}; 474 475WidgetClass panedWidgetClass = (WidgetClass)&panedClassRec; 476WidgetClass vPanedWidgetClass = (WidgetClass)&panedClassRec; 477 478/* 479 * Implementation 480 */ 481/* Function: 482 * AdjustPanedSize 483 * 484 * Parameters: 485 * pw - paned widget to adjust 486 * off_size - new off_size to use 487 * result_ret - result of query (return) 488 * on_size_ret - new on_size (return) 489 * off_size_ret - new off_size (return) 490 * 491 * Description: 492 * Adjusts the size of the pane. 493 * 494 * Returns: 495 * amount of change in size 496 */ 497static void 498AdjustPanedSize(PanedWidget pw, unsigned int off_size, 499 XtGeometryResult *result_ret, 500 Dimension *on_size_ret, Dimension *off_size_ret) 501{ 502 Dimension old_size = PaneSize((Widget)pw, IsVert(pw)); 503 Dimension newsize = 0; 504 Widget *childP; 505 XtWidgetGeometry request, reply; 506 507 request.request_mode = CWWidth | CWHeight; 508 509 ForAllPanes(pw, childP) { 510 int size = Max(PaneInfo(*childP)->size, (int)PaneInfo(*childP)->min); 511 512 AssignMin(size, (int)PaneInfo(*childP)->max); 513 newsize += size + pw->paned.internal_bw; 514 } 515 newsize -= pw->paned.internal_bw; 516 517 if (newsize < 1) 518 newsize = 1; 519 520 if (IsVert(pw)) { 521 request.width = off_size; 522 request.height = newsize; 523 } 524 else { 525 request.width = newsize; 526 request.height = off_size; 527 } 528 529 if (result_ret != NULL) { 530 request.request_mode |= XtCWQueryOnly; 531 532 *result_ret = XtMakeGeometryRequest((Widget)pw, &request, &reply); 533 _XawImCallVendorShellExtResize((Widget)pw); 534 535 if (newsize == old_size || *result_ret == XtGeometryNo) { 536 *on_size_ret = old_size; 537 *off_size_ret = off_size; 538 return; 539 } 540 if (*result_ret != XtGeometryAlmost) { 541 *on_size_ret = GetRequestInfo(&request, IsVert(pw)); 542 *off_size_ret = GetRequestInfo(&request, !IsVert(pw)); 543 return; 544 } 545 *on_size_ret = GetRequestInfo(&reply, IsVert(pw)); 546 *off_size_ret = GetRequestInfo(&reply, !IsVert(pw)); 547 return; 548 } 549 550 if (newsize == old_size) 551 return; 552 553 if (XtMakeGeometryRequest((Widget)pw, &request, &reply) == XtGeometryAlmost) 554 XtMakeGeometryRequest((Widget)pw, &reply, &request); 555} 556 557/* 558 * Function: 559 * ChoosePaneToResize. 560 * 561 * Parameters: 562 * pw - paned widget 563 * paneindex - index of the current pane 564 * dir - direction to search first 565 * shrink - True if we need to shrink a pane, False otherwise 566 * 567 * Description: 568 * This function chooses a pane to resize. 569 They are chosen using the following rules: 570 * 571 * 1) size < max && size > min 572 * 2) skip adjust == False 573 * 3) widget not its prefered height 574 * && this change will bring it closer 575 * && The user has not resized this pane. 576 * 577 * If no widgets are found that fits all the rules then 578 * rule #3 is broken. 579 * If there are still no widgets found than 580 * rule #2 is broken. 581 * Rule #1 is never broken. 582 * If no widgets are found then NULL is returned. 583 * 584 * Returns: 585 * pane to resize or NULL 586 */ 587static Pane 588ChoosePaneToResize(PanedWidget pw, int paneindex, Direction dir, Bool shrink) 589{ 590 Widget *childP; 591 int rules = 3; 592 Direction _dir = dir; 593 int _index = paneindex; 594 595 if (paneindex == NO_INDEX || dir == AnyPane) { /* Use defaults */ 596 _dir = LowRightPane; /* Go up - really */ 597 _index = pw->paned.num_panes - 1; /* Start the last pane, and work 598 backwards */ 599 } 600 childP = pw->composite.children + _index; 601 602 /*CONSTCOND*/ 603 while(True) { 604 Pane pane = PaneInfo(*childP); 605 606 if ((rules < 3 || SatisfiesRule3(pane, shrink)) 607 && (rules < 2 || SatisfiesRule2(pane)) 608 && SatisfiesRule1(pane, shrink) 609 && (paneindex != PaneIndex(*childP) || dir == AnyPane)) 610 return (pane); 611 612 /* 613 * This is counter-intuitive, but if we are resizing the pane 614 * above the grip we want to choose a pane below the grip to lose, 615 * and visa-versa 616 */ 617 if (_dir == LowRightPane) 618 --childP; 619 else 620 ++childP; 621 622 /* 623 * If we have come to and edge then reduce the rule set, and try again 624 * If we are reduced the rules to none, then return NULL 625 */ 626 if ((childP - pw->composite.children) < 0 || 627 (childP - pw->composite.children) >= pw->paned.num_panes) { 628 if (--rules < 1) /* less strict rules */ 629 return (NULL); 630 childP = pw->composite.children + _index; 631 } 632 } 633} 634 635/* 636 * Function: 637 * LoopAndRefigureChildren 638 * 639 * Parameters: 640 * pw - paned widget 641 * paneindex - number of the pane border we are moving 642 * dir - pane to move (either UpLeftPane or LowRightPane) 643 * sizeused - current amount of space used (used and returned) 644 * 645 * Description: 646 * If we are resizing either the UpleftPane or LowRight Pane loop 647 * through all the children to see if any will allow us to resize them. 648 */ 649static void 650LoopAndRefigureChildren(PanedWidget pw, int paneindex, Direction dir, 651 int *sizeused) 652{ 653 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw)); 654 Boolean shrink = (*sizeused > pane_size); 655 656 if (dir == LowRightPane) 657 paneindex++; 658 659 /* While all panes do not fit properly */ 660 while (*sizeused != pane_size) { 661 /* 662 * Choose a pane to resize 663 * First look on the Pane Stack, and then go hunting for another one 664 * If we fail to find a pane to resize then give up 665 */ 666 Pane pane; 667 int start_size; 668 Dimension old; 669 Boolean rule3_ok = False, from_stack = True; 670 671 GetPaneStack(pw, shrink, &pane, &start_size); 672 if (pane == NULL) { 673 pane = ChoosePaneToResize(pw, paneindex, dir, shrink); 674 if (pane == NULL) 675 return; /* no one to resize, give up */ 676 677 rule3_ok = SatisfiesRule3(pane, shrink); 678 from_stack = False; 679 PushPaneStack(pw, pane); 680 } 681 682 /* 683 * Try to resize this pane so that all panes will fit, take min and max 684 * into account 685 */ 686 old = pane->size; 687 pane->size += pane_size - *sizeused; 688 689 if (from_stack) { 690 if (shrink) { 691 AssignMax(pane->size, start_size); 692 } /* don't remove these braces */ 693 else 694 AssignMin(pane->size, start_size); 695 696 if (pane->size == start_size) 697 (void)PopPaneStack(pw); 698 } 699 else if (rule3_ok) { 700 if (shrink) { 701 AssignMax(pane->size, (int)pane->wp_size); 702 } /* don't remove these braces */ 703 else 704 AssignMin(pane->size, (int)pane->wp_size); 705 } 706 707 pane->paned_adjusted_me = pane->size != pane->wp_size; 708 AssignMax(pane->size, (int)pane->min); 709 AssignMin(pane->size, (int)pane->max); 710 *sizeused += (pane->size - old); 711 } 712} 713 714/* 715 * Function: 716 * RefigureLocations 717 * 718 * Parameters: 719 * pw - paned widget 720 * paneindex - child to start refiguring at 721 * dir - direction to move from child 722 * 723 * Description: 724 * Refigures all locations of children. 725 * There are special arguments to paneindex and dir, they are: 726 * paneindex - NO_INDEX. 727 * dir - AnyPane. 728 * 729 * If either of these is true then all panes may be resized and 730 * the choosing of panes procedes in reverse order starting with the 731 * last child. 732 */ 733static void 734RefigureLocations(PanedWidget pw, int paneindex, Direction dir) 735{ 736 Widget *childP; 737 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw)); 738 int sizeused = 0; 739 Position loc = 0; 740 741 if (pw->paned.num_panes == 0 || !pw->paned.refiguremode) 742 return; 743 744 /* 745 * Get an initial estimate of the size we will use 746 */ 747 ForAllPanes(pw, childP) { 748 Pane pane = PaneInfo(*childP); 749 750 AssignMax(pane->size, (int) pane->min); 751 AssignMin(pane->size, (int) pane->max); 752 sizeused += (int)pane->size + (int)pw->paned.internal_bw; 753 } 754 sizeused -= (int)pw->paned.internal_bw; 755 756 if (dir != ThisBorderOnly && sizeused != pane_size) 757 LoopAndRefigureChildren(pw, paneindex, dir, &sizeused); 758 759 /* 760 * If we still are not the right size, then tell the pane that 761 * wanted to resize that it can't 762 */ 763 if (paneindex != NO_INDEX && dir != AnyPane) { 764 Pane pane = PaneInfo(*(pw->composite.children + paneindex)); 765 Dimension old = pane->size; 766 767 pane->size += pane_size - sizeused; 768 AssignMax(pane->size, (int) pane->min); 769 AssignMin(pane->size, (int) pane->max); 770 sizeused += pane->size - old; 771 } 772 773 /* 774 * It is possible that the panes will not fit inside the vpaned widget, but 775 * we have tried out best 776 * 777 * Assign each pane a location 778 */ 779 ForAllPanes(pw, childP) { 780 PaneInfo(*childP)->delta = loc; 781 loc += PaneInfo(*childP)->size + pw->paned.internal_bw; 782 } 783} 784 785/* 786 * Function: 787 * CommitNewLocations 788 * 789 * Parameters: 790 * pw - paned widget 791 * 792 * Description: 793 * Commits all of the previously figured locations. 794 */ 795static void 796CommitNewLocations(PanedWidget pw) 797{ 798 Widget *childP; 799 XWindowChanges changes; 800 801 changes.stack_mode = Above; 802 803 ForAllPanes(pw, childP) { 804 Pane pane = PaneInfo(*childP); 805 Widget grip = pane->grip; /* may be NULL */ 806 807 if (IsVert(pw)) { 808 XtMoveWidget(*childP, (Position) 0, pane->delta); 809 XtResizeWidget(*childP, XtWidth(pw), pane->size, 0); 810 811 if (HasGrip(*childP)) { /* Move and Display the Grip */ 812 changes.x = XtWidth(pw) - pw->paned.grip_indent - 813 XtWidth(grip) - (XtBorderWidth(grip) << 1); 814 changes.y = XtY(*childP) + XtHeight(*childP) - 815 (XtHeight(grip) >> 1) - XtBorderWidth(grip) + 816 (pw->paned.internal_bw >> 1); 817 } 818 } 819 else { 820 XtMoveWidget(*childP, pane->delta, 0); 821 XtResizeWidget(*childP, pane->size, XtHeight(pw), 0); 822 823 if (HasGrip(*childP)) { /* Move and Display the Grip */ 824 changes.x = XtX(*childP) + XtWidth(*childP) - 825 (XtWidth(grip) >> 1) - XtBorderWidth(grip) + 826 (pw->paned.internal_bw >> 1); 827 changes.y = XtHeight(pw) - pw->paned.grip_indent - 828 XtHeight(grip) - (XtBorderWidth(grip) << 1); 829 } 830 } 831 832 /* 833 * This should match XtMoveWidget, except that we're also insuring the 834 * grip is Raised in the same request 835 */ 836 837 if (HasGrip(*childP)) { 838 XtX(grip) = changes.x; 839 XtY(grip) = changes.y; 840 841 if (XtIsRealized(pane->grip)) 842 XConfigureWindow(XtDisplay(pane->grip), XtWindow(pane->grip), 843 CWX | CWY | CWStackMode, &changes); 844 } 845 } 846 ClearPaneStack(pw); 847} 848 849/* 850 * Function: 851 * RefigureLocationsAndCommit 852 * 853 * Parameters: 854 * pw - paned widget 855 * 856 * Description: 857 * Refigures all locations in a paned widget and commits them immediately. 858 * 859 * This function does nothing if any of the following are true. 860 * o refiguremode is false. 861 * o The widget is unrealized. 862 * o There are no panes is the paned widget. 863 */ 864static void 865RefigureLocationsAndCommit(Widget w) 866{ 867 PanedWidget pw = (PanedWidget)w; 868 869 if (pw->paned.refiguremode && XtIsRealized(w) && pw->paned.num_panes > 0) { 870 RefigureLocations(pw, NO_INDEX, AnyPane); 871 CommitNewLocations(pw); 872 } 873} 874 875/* 876 * Function: 877 * _DrawRect 878 * 879 * Parameters: 880 * pw - paned widget 881 * gc - gc to used for the draw 882 * on_olc - location of upper left corner of rect 883 * off_loc - "" 884 * on_size - size of rectangle 885 * off_size - "" 886 * 887 * Description: 888 * Draws a rectangle in the proper orientation. 889 */ 890static void 891_DrawRect(PanedWidget pw, GC gc, int on_loc, int off_loc, 892 unsigned int on_size, unsigned int off_size) 893{ 894 if (IsVert(pw)) 895 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 896 off_loc, on_loc, off_size, on_size); 897 else 898 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 899 on_loc, off_loc, on_size, off_size); 900} 901 902/* 903 * Function: 904 * _DrawInternalBorders 905 * 906 * Parameters: 907 * pw - paned widget 908 * gc - GC to use to draw the borders 909 * 910 * Description: 911 * Draws the internal borders into the paned widget. 912 */ 913static void 914_DrawInternalBorders(PanedWidget pw, GC gc) 915{ 916 Widget *childP; 917 int on_loc, off_loc; 918 unsigned int on_size, off_size; 919 920 /* 921 * This is an optimization. Do not paint the internal borders if 922 * they are the same color as the background 923 */ 924 if (pw->core.background_pixel == pw->paned.internal_bp) 925 return; 926 927 off_loc = 0; 928 off_size = (unsigned int) PaneSize((Widget)pw, !IsVert(pw)); 929 on_size = (unsigned int)pw->paned.internal_bw; 930 931 ForAllPanes(pw, childP) { 932 on_loc = IsVert(pw) ? XtY(*childP) : XtX(*childP); 933 on_loc -= (int)on_size; 934 935 _DrawRect(pw, gc, on_loc, off_loc, on_size, off_size); 936 } 937} 938 939#define DrawInternalBorders(pw) \ 940 _DrawInternalBorders((pw), (pw)->paned.normgc) 941#define EraseInternalBorders(pw) \ 942 _DrawInternalBorders((pw), (pw)->paned.invgc) 943/* 944 * Function Name: 945 * _DrawTrackLines 946 * 947 * Parameters: 948 * pw - Paned widget 949 * erase - if True then just erase track lines, else draw them in 950 * 951 * Description: 952 * Draws the lines that animate the pane borders when the grips are moved. 953 */ 954static void 955_DrawTrackLines(PanedWidget pw, Bool erase) 956{ 957 Widget *childP; 958 Pane pane; 959 int on_loc, off_loc; 960 unsigned int on_size, off_size; 961 962 off_loc = 0; 963 off_size = PaneSize((Widget)pw, !IsVert(pw)); 964 965 ForAllPanes(pw, childP) { 966 pane = PaneInfo(*childP); 967 if (erase || pane->olddelta != pane->delta) { 968 on_size = pw->paned.internal_bw; 969 if (!erase) { 970 on_loc = PaneInfo(*childP)->olddelta - (int) on_size; 971 _DrawRect(pw, pw->paned.flipgc, 972 on_loc, off_loc, on_size, off_size); 973 } 974 975 on_loc = PaneInfo(*childP)->delta - (int)on_size; 976 977 _DrawRect(pw, pw->paned.flipgc, 978 on_loc, off_loc, on_size, off_size); 979 980 pane->olddelta = pane->delta; 981 } 982 } 983} 984 985#define DrawTrackLines(pw) _DrawTrackLines((pw), False); 986#define EraseTrackLines(pw) _DrawTrackLines((pw), True); 987/* 988 * Function: 989 * GetEventLocation 990 * 991 * Parameters: 992 * pw - the paned widget 993 * event - pointer to an event 994 * 995 * Description: 996 * Converts and event to an x and y location. 997 * 998 * Returns: 999 * if this is a vertical pane then (y) else (x) 1000 */ 1001static int 1002GetEventLocation(PanedWidget pw, XEvent *event) 1003{ 1004 int x, y; 1005 1006 switch (event->xany.type) { 1007 case ButtonPress: 1008 case ButtonRelease: 1009 x = event->xbutton.x_root; 1010 y = event->xbutton.y_root; 1011 break; 1012 case KeyPress: 1013 case KeyRelease: 1014 x = event->xkey.x_root; 1015 y = event->xkey.y_root; 1016 break; 1017 case MotionNotify: 1018 x = event->xmotion.x_root; 1019 y = event->xmotion.y_root; 1020 break; 1021 default: 1022 x = pw->paned.start_loc; 1023 y = pw->paned.start_loc; 1024 } 1025 1026 if (IsVert(pw)) 1027 return (y); 1028 1029 return (x); 1030} 1031 1032/* 1033 * Function: 1034 * StartGripAdjustment 1035 * 1036 * Parameters: 1037 * pw - paned widget 1038 * grip - grip widget selected 1039 * dir - direction that we are to be moving 1040 * 1041 * Description: 1042 * Starts the grip adjustment procedure. 1043 */ 1044static void 1045StartGripAdjustment(PanedWidget pw, Widget grip, Direction dir) 1046{ 1047 Widget *childP; 1048 Cursor cursor; 1049 1050 pw->paned.whichadd = pw->paned.whichsub = NULL; 1051 1052 if (dir == ThisBorderOnly || dir == UpLeftPane) 1053 pw->paned.whichadd = pw->composite.children[PaneIndex(grip)]; 1054 if (dir == ThisBorderOnly || dir == LowRightPane) 1055 pw->paned.whichsub = pw->composite.children[PaneIndex(grip) + 1]; 1056 1057 /* 1058 * Change the cursor 1059 */ 1060 if (XtIsRealized(grip)) { 1061 if (IsVert(pw)) { 1062 if (dir == UpLeftPane) 1063 cursor = pw->paned.adjust_upper_cursor; 1064 else if (dir == LowRightPane) 1065 cursor = pw->paned.adjust_lower_cursor; 1066 else { 1067 if (pw->paned.adjust_this_cursor == None) 1068 cursor = pw->paned.v_adjust_this_cursor; 1069 else 1070 cursor = pw->paned.adjust_this_cursor; 1071 } 1072 } 1073 else { 1074 if (dir == UpLeftPane) 1075 cursor = pw->paned.adjust_left_cursor; 1076 else if (dir == LowRightPane) 1077 cursor = pw->paned.adjust_right_cursor; 1078 else { 1079 if (pw->paned.adjust_this_cursor == None) 1080 cursor = pw->paned.h_adjust_this_cursor; 1081 else 1082 cursor = pw->paned.adjust_this_cursor; 1083 } 1084 } 1085 1086 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1087 } 1088 1089 EraseInternalBorders(pw); 1090 ForAllPanes(pw, childP) 1091 PaneInfo(*childP)->olddelta = -99; 1092 1093 EraseTrackLines(pw); 1094} 1095 1096/* 1097 * Function: 1098 * MoveGripAdjustment 1099 * 1100 * Parameters: 1101 * pw - paned widget 1102 * grip - grip that we are moving 1103 * dir - direction the pane we are interested is w.r.t the grip 1104 * loc - location of pointer in proper direction 1105 * 1106 * Description: 1107 * This routine moves all panes around when a grip is moved. 1108 */ 1109static void 1110MoveGripAdjustment(PanedWidget pw, Widget grip, Direction dir, int loc) 1111{ 1112 int diff, add_size = 0, sub_size = 0; 1113 1114 diff = loc - pw->paned.start_loc; 1115 1116 if (pw->paned.whichadd) 1117 add_size = PaneSize(pw->paned.whichadd, IsVert(pw)) + diff; 1118 1119 if (pw->paned.whichsub) 1120 sub_size = PaneSize(pw->paned.whichsub, IsVert(pw)) - diff; 1121 1122 /* 1123 * If moving this border only then do not allow either of the borders 1124 * to go beyond the min or max size allowed 1125 */ 1126 if (dir == ThisBorderOnly) { 1127 int old_add_size = add_size, old_sub_size; 1128 1129 AssignMax(add_size, (int)PaneInfo(pw->paned.whichadd)->min); 1130 AssignMin(add_size, (int)PaneInfo(pw->paned.whichadd)->max); 1131 if (add_size != old_add_size) 1132 sub_size += old_add_size - add_size; 1133 1134 old_sub_size = sub_size; 1135 AssignMax(sub_size, (int)PaneInfo(pw->paned.whichsub)->min); 1136 AssignMin(sub_size, (int)PaneInfo(pw->paned.whichsub)->max); 1137 if (sub_size != old_sub_size) 1138 return; /* Abort to current sizes */ 1139 } 1140 1141 if (add_size != 0) 1142 PaneInfo(pw->paned.whichadd)->size = add_size; 1143 if (sub_size != 0) 1144 PaneInfo(pw->paned.whichsub)->size = sub_size; 1145 RefigureLocations(pw, PaneIndex(grip), dir); 1146 DrawTrackLines(pw); 1147} 1148 1149/* 1150 * Function: 1151 * CommitGripAdjustment 1152 * 1153 * Parameters: 1154 * pw - paned widget 1155 * 1156 * Description: 1157 * Commits the grip adjustment. 1158 */ 1159static void 1160CommitGripAdjustment(PanedWidget pw) 1161{ 1162 EraseTrackLines(pw); 1163 CommitNewLocations(pw); 1164 DrawInternalBorders(pw); 1165 1166 /* 1167 * Since the user selected this size then use it as the preferred size 1168 */ 1169 if (pw->paned.whichadd) { 1170 Pane pane = PaneInfo(pw->paned.whichadd); 1171 1172 pane->wp_size = pane->size; 1173 } 1174 if (pw->paned.whichsub) { 1175 Pane pane = PaneInfo(pw->paned.whichsub); 1176 1177 pane->wp_size = pane->size; 1178 } 1179} 1180 1181/* 1182 * Function: 1183 * HandleGrip 1184 * 1185 * Parameters: 1186 * grip - grip widget that has been moved 1187 * temp - (not used) 1188 * call_data - data passed to us from the grip widget 1189 * 1190 * Description: 1191 * Handles the grip manipulations. 1192 */ 1193/*ARGSUSED*/ 1194static void 1195HandleGrip(Widget grip, XtPointer temp, XtPointer callData) 1196{ 1197 XawGripCallData call_data = (XawGripCallData)callData; 1198 PanedWidget pw = (PanedWidget) XtParent(grip); 1199 int loc; 1200 char action_type[2], direction[2]; 1201 Cursor cursor; 1202 Arg arglist[1]; 1203 1204 if (call_data->num_params) 1205 XmuNCopyISOLatin1Uppered(action_type, call_data->params[0], 1206 sizeof(action_type)); 1207 1208 if (call_data->num_params == 0 1209 || (action_type[0] == 'C' && call_data->num_params != 1) 1210 || (action_type[0] != 'C' && call_data->num_params != 2)) 1211 XtAppError(XtWidgetToApplicationContext(grip), 1212 "Paned GripAction has been passed incorrect parameters."); 1213 1214 loc = GetEventLocation(pw, (XEvent *)call_data->event); 1215 1216 if (action_type[0] != 'C') 1217 XmuNCopyISOLatin1Uppered(direction, call_data->params[1], 1218 sizeof(direction)); 1219 1220 switch (action_type[0]) { 1221 case 'S': /* Start adjustment */ 1222 pw->paned.resize_children_to_pref = False; 1223 StartGripAdjustment(pw, grip, (Direction)direction[0]); 1224 pw->paned.start_loc = loc; 1225 break; 1226 case 'M': 1227 MoveGripAdjustment(pw, grip, (Direction)direction[0], loc); 1228 break; 1229 case 'C': 1230 XtSetArg(arglist[0], XtNcursor, &cursor); 1231 XtGetValues(grip, arglist, 1); 1232 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1233 CommitGripAdjustment(pw); 1234 break; 1235 default: 1236 XtAppError(XtWidgetToApplicationContext(grip), 1237 "Paned GripAction(); 1st parameter invalid"); 1238 break; 1239 } 1240} 1241 1242/* 1243 * Function: 1244 * ResortChildren 1245 * 1246 * Arguments: 1247 * pw - paned widget 1248 * 1249 * Description: 1250 * Resorts the children so that all managed children are first. 1251 */ 1252static void 1253ResortChildren(PanedWidget pw) 1254{ 1255 Widget *unmanagedP, *childP; 1256 1257 unmanagedP = NULL; 1258 ForAllChildren(pw, childP) { 1259 if (!IsPane(*childP) || !XtIsManaged(*childP)) { 1260 /* 1261 * We only keep track of the first unmanaged pane 1262 */ 1263 if (unmanagedP == NULL) 1264 unmanagedP = childP; 1265 } 1266 else { /* must be a managed pane */ 1267 /* 1268 * If an earlier widget was not a managed pane, then swap 1269 */ 1270 if (unmanagedP != NULL) { 1271 Widget child = *unmanagedP; 1272 1273 *unmanagedP = *childP; 1274 *childP = child; 1275 childP = unmanagedP; /* easiest to just back-track */ 1276 unmanagedP = NULL; /* in case there is another managed */ 1277 } 1278 } 1279 } 1280} 1281 1282/* 1283 * Function: 1284 * ManageAndUnmanageGrips 1285 * 1286 * Parameters: 1287 * pw - paned widget 1288 * 1289 * Description: 1290 * This function manages and unmanages the grips so that 1291 * the managed state of each grip matches that of its pane. 1292 */ 1293static void 1294ManageAndUnmanageGrips(PanedWidget pw) 1295{ 1296 WidgetList managed_grips, unmanaged_grips; 1297 Widget *managedP, *unmanagedP, *childP; 1298 Cardinal alloc_size; 1299 1300 alloc_size = sizeof(Widget) * (pw->composite.num_children >> 1); 1301 managedP = managed_grips = (WidgetList)XtMalloc(alloc_size); 1302 unmanagedP = unmanaged_grips = (WidgetList)XtMalloc(alloc_size); 1303 1304 ForAllChildren(pw, childP) 1305 if (IsPane(*childP) && HasGrip(*childP)) { 1306 if (XtIsManaged(*childP)) 1307 *managedP++ = PaneInfo(*childP)->grip; 1308 else 1309 *unmanagedP++ = PaneInfo(*childP)->grip; 1310 } 1311 1312 if (managedP != managed_grips) { 1313 *unmanagedP++ = *--managedP; /* Last grip is never managed */ 1314 XtManageChildren(managed_grips, managedP - managed_grips); 1315 } 1316 1317 if (unmanagedP != unmanaged_grips) 1318 XtUnmanageChildren(unmanaged_grips, unmanagedP - unmanaged_grips); 1319 1320 XtFree((char *)managed_grips); 1321 XtFree((char *)unmanaged_grips); 1322} 1323 1324/* 1325 * Function: 1326 * CreateGrip 1327 * 1328 * Parameters: 1329 * child - child that wants a grip to be created for it 1330 * 1331 * Description: 1332 * Creates a grip widget. 1333 */ 1334static void 1335CreateGrip(Widget child) 1336{ 1337 PanedWidget pw = (PanedWidget)XtParent(child); 1338 Arg arglist[2]; 1339 Cardinal num_args = 0; 1340 Cursor cursor; 1341 1342 XtSetArg(arglist[num_args], XtNtranslations, pw->paned.grip_translations); 1343 num_args++; 1344 if ((cursor = pw->paned.grip_cursor) == None) { 1345 if (IsVert(pw)) 1346 cursor = pw->paned.v_grip_cursor; 1347 else 1348 cursor = pw->paned.h_grip_cursor; 1349 } 1350 1351 XtSetArg(arglist[num_args], XtNcursor, cursor); 1352 num_args++; 1353 PaneInfo(child)->grip = XtCreateWidget("grip", gripWidgetClass, (Widget)pw, 1354 arglist, num_args); 1355 1356 XtAddCallback(PaneInfo(child)->grip, XtNcallback, 1357 HandleGrip, (XtPointer)child); 1358} 1359 1360/* 1361 * Function: 1362 * GetGCs 1363 * 1364 * Parameters: 1365 * w - paned widget 1366 */ 1367static void 1368GetGCs(Widget w) 1369{ 1370 PanedWidget pw = (PanedWidget)w; 1371 XtGCMask valuemask; 1372 XGCValues values; 1373 1374 /* 1375 * Draw pane borders in internal border color 1376 */ 1377 values.foreground = pw->paned.internal_bp; 1378 valuemask = GCForeground; 1379 pw->paned.normgc = XtGetGC(w, valuemask, &values); 1380 1381 /* 1382 * Erase pane borders with background color 1383 */ 1384 values.foreground = pw->core.background_pixel; 1385 valuemask = GCForeground; 1386 pw->paned.invgc = XtGetGC(w, valuemask, &values); 1387 1388 /* 1389 * Draw Track lines (animate pane borders) in 1390 * internal border color ^ bg color 1391 */ 1392 values.function = GXinvert; 1393 values.plane_mask = pw->paned.internal_bp ^ pw->core.background_pixel; 1394 values.subwindow_mode = IncludeInferiors; 1395 valuemask = GCPlaneMask | GCFunction | GCSubwindowMode; 1396 pw->paned.flipgc = XtGetGC(w, valuemask, &values); 1397} 1398 1399/* 1400 * Function: 1401 * SetChildrenPrefSizes 1402 * 1403 * Parameters: 1404 * pw - paned widget 1405 * 1406 * Description: 1407 * Sets the preferred sizes of the children. 1408 */ 1409static void 1410SetChildrenPrefSizes(PanedWidget pw, unsigned int off_size) 1411{ 1412 Widget *childP; 1413 Boolean vert = IsVert(pw); 1414 XtWidgetGeometry request, reply; 1415 1416 ForAllPanes(pw, childP) 1417 if (pw->paned.resize_children_to_pref || PaneInfo(*childP)->size == 0 || 1418 PaneInfo(*childP)->resize_to_pref) { 1419 if (PaneInfo(*childP)->preferred_size != PANED_ASK_CHILD) 1420 PaneInfo(*childP)->wp_size = PaneInfo(*childP)->preferred_size; 1421 else { 1422 if(vert) { 1423 request.request_mode = CWWidth; 1424 request.width = off_size; 1425 } 1426 else { 1427 request.request_mode = CWHeight; 1428 request.height = off_size; 1429 } 1430 1431 if ((XtQueryGeometry(*childP, &request, &reply) 1432 == XtGeometryAlmost) 1433 && (reply.request_mode = (vert ? CWHeight : CWWidth))) 1434 PaneInfo(*childP)->wp_size = GetRequestInfo(&reply, vert); 1435 else 1436 PaneInfo(*childP)->wp_size = PaneSize(*childP, vert); 1437 } 1438 1439 PaneInfo(*childP)->size = PaneInfo(*childP)->wp_size; 1440 } 1441} 1442 1443/* 1444 * Function: 1445 * ChangeAllGripCursors 1446 * 1447 * Parameters: 1448 * pw - paned widget 1449 * 1450 * Description: 1451 * Changes all the grip cursors. 1452 */ 1453static void 1454ChangeAllGripCursors(PanedWidget pw) 1455{ 1456 Widget *childP; 1457 1458 ForAllPanes(pw, childP) { 1459 Arg arglist[1]; 1460 Cursor cursor; 1461 1462 if ((cursor = pw->paned.grip_cursor) == None) { 1463 if (IsVert(pw)) 1464 cursor = pw->paned.v_grip_cursor; 1465 else 1466 cursor = pw->paned.h_grip_cursor; 1467 } 1468 1469 if (HasGrip(*childP)) { 1470 XtSetArg(arglist[0], XtNcursor, cursor); 1471 XtSetValues(PaneInfo(*childP)->grip, arglist, 1); 1472 } 1473 } 1474} 1475 1476/* 1477 * Function: 1478 * PushPaneStack 1479 * 1480 * Parameters: 1481 * pw - paned widget 1482 * pane - pane that we are pushing 1483 * 1484 * Description: 1485 * Pushes a value onto the pane stack. 1486 */ 1487static void 1488PushPaneStack(PanedWidget pw, Pane pane) 1489{ 1490 PaneStack *stack = (PaneStack *)XtMalloc(sizeof(PaneStack)); 1491 1492 stack->next = pw->paned.stack; 1493 stack->pane = pane; 1494 stack->start_size = pane->size; 1495 1496 pw->paned.stack = stack; 1497} 1498 1499/* 1500 * Function: 1501 * GetPaneStack 1502 * 1503 * Parameters: 1504 * pw - paned widget 1505 * shrink - True if we want to shrink this pane, False otherwise 1506 * pane - pane that we are popping (return) 1507 * start_size - size that this pane started at (return) 1508 * 1509 * Description: 1510 * Gets the top value from the pane stack. 1511 */ 1512static void 1513GetPaneStack(PanedWidget pw, Bool shrink, Pane *pane, int *start_size) 1514{ 1515 if (pw->paned.stack == NULL) { 1516 *pane = NULL; 1517 return; 1518 } 1519 1520 *pane = pw->paned.stack->pane; 1521 *start_size = pw->paned.stack->start_size; 1522 1523 if (shrink != ((*pane)->size > *start_size)) 1524 *pane = NULL; 1525} 1526 1527/* 1528 * Function: 1529 * PopPaneStack 1530 * 1531 * Parameters: 1532 * pw - paned widget 1533 * 1534 * Description: 1535 * Pops the top item off the pane stack. 1536 * 1537 * Returns: True if this is not the last element on the stack 1538 */ 1539static Bool 1540PopPaneStack(PanedWidget pw) 1541{ 1542 PaneStack *stack = pw->paned.stack; 1543 1544 if (stack == NULL) 1545 return (False); 1546 1547 pw->paned.stack = stack->next; 1548 XtFree((char *)stack); 1549 1550 if (pw->paned.stack == NULL) 1551 return (False); 1552 1553 return (True); 1554} 1555 1556/* 1557 * Function: 1558 * ClearPaneStack 1559 * 1560 * Parameters: 1561 * pw - paned widget 1562 * 1563 * Description: 1564 * Removes all entries from the pane stack. 1565 */ 1566static void 1567ClearPaneStack(PanedWidget pw) 1568{ 1569 while(PopPaneStack(pw)) 1570 ; 1571} 1572 1573static void 1574XawPanedClassInitialize(void) 1575{ 1576 XawInitializeWidgetSet(); 1577 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, 1578 NULL, 0); 1579 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString, 1580 NULL, 0, XtCacheNone, NULL); 1581} 1582 1583/* The Geometry Manager only allows changes after Realize if 1584 * allow_resize is True in the constraints record. 1585 * 1586 * For vertically paned widgets: 1587 * 1588 * It only allows height changes, but offers the requested height 1589 * as a compromise if both width and height changes were requested. 1590 * 1591 * For horizontal widgets the converse is true. 1592 * As all good Geometry Managers should, we will return No if the 1593 * request will have no effect; i.e. when the requestor is already 1594 * of the desired geometry. 1595 */ 1596static XtGeometryResult 1597XawPanedGeometryManager(Widget w, XtWidgetGeometry *request, 1598 XtWidgetGeometry *reply) 1599{ 1600 PanedWidget pw = (PanedWidget)XtParent(w); 1601 XtGeometryMask mask = request->request_mode; 1602 Dimension old_size, old_wpsize, old_paned_size; 1603 Pane pane = PaneInfo(w); 1604 Boolean vert = IsVert(pw); 1605 Dimension on_size, off_size; 1606 XtGeometryResult result; 1607 Boolean almost = False; 1608 1609 /* 1610 * If any of the following is true, disallow the geometry change 1611 * 1612 * o The paned widget is realized and allow_resize is false for the pane 1613 * o The child did not ask to change the on_size 1614 * o The request is not a width or height request 1615 * o The requested size is the same as the current size 1616 */ 1617 1618 if ((XtIsRealized((Widget)pw) && !pane->allow_resize) 1619 || !(mask & (vert ? CWHeight : CWWidth)) 1620 ||(mask & ~(CWWidth | CWHeight)) 1621 || GetRequestInfo(request, vert) == PaneSize(w, vert)) 1622 return (XtGeometryNo); 1623 1624 old_paned_size = PaneSize((Widget)pw, vert); 1625 old_wpsize = pane->wp_size; 1626 old_size = pane->size; 1627 1628 pane->wp_size = pane->size = GetRequestInfo(request, vert); 1629 1630 AdjustPanedSize(pw, PaneSize((Widget)pw, !vert), &result, &on_size, 1631 &off_size); 1632 1633 /* 1634 * Fool the Refigure Locations proc to thinking that we are 1635 * a different on_size 1636 */ 1637 1638 if (result != XtGeometryNo) { 1639 if (vert) 1640 XtHeight(pw) = on_size; 1641 else 1642 XtWidth(pw) = on_size; 1643 } 1644 1645 RefigureLocations(pw, PaneIndex(w), AnyPane); 1646 1647 /* 1648 * Set up reply struct and reset core on_size 1649 */ 1650 if (vert) { 1651 XtHeight(pw) = old_paned_size; 1652 reply->height = pane->size; 1653 reply->width = off_size; 1654 } 1655 else { 1656 XtWidth(pw) = old_paned_size; 1657 reply->height = off_size; 1658 reply->width = pane->size; 1659 } 1660 1661 /* 1662 * IF either of the following is true 1663 * 1664 * o There was a "off_size" request and the new "off_size" is different 1665 * from that requested 1666 * o There was no "off_size" request and the new "off_size" is different 1667 * 1668 * o The "on_size" we will allow is different from that requested 1669 * 1670 * THEN: set almost 1671 */ 1672 if (!((vert ? CWWidth : CWHeight) & mask)) { 1673 if (vert) 1674 request->width = XtWidth(w); 1675 else 1676 request->height = XtHeight(w); 1677 } 1678 1679 almost = GetRequestInfo(request, !vert) != GetRequestInfo(reply, !vert); 1680 almost |= (GetRequestInfo(request, vert) != GetRequestInfo(reply, vert)); 1681 1682 if ((mask & XtCWQueryOnly) || almost) { 1683 pane->wp_size = old_wpsize; 1684 pane->size = old_size; 1685 RefigureLocations(pw, PaneIndex(w), AnyPane); 1686 reply->request_mode = CWWidth | CWHeight; 1687 if (almost) 1688 return (XtGeometryAlmost); 1689 } 1690 else { 1691 AdjustPanedSize(pw, PaneSize((Widget) pw, !vert), NULL, NULL, NULL); 1692 CommitNewLocations(pw); /* layout already refigured */ 1693 } 1694 1695 return (XtGeometryDone); 1696} 1697 1698/*ARGSUSED*/ 1699static void 1700XawPanedInitialize(Widget request, Widget cnew, 1701 ArgList args, Cardinal *num_args) 1702{ 1703 PanedWidget pw = (PanedWidget)cnew; 1704 1705 GetGCs((Widget)pw); 1706 1707 pw->paned.recursively_called = False; 1708 pw->paned.stack = NULL; 1709 pw->paned.resize_children_to_pref = True; 1710 pw->paned.num_panes = 0; 1711} 1712 1713static void 1714XawPanedRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes) 1715{ 1716 PanedWidget pw = (PanedWidget)w; 1717 Widget *childP; 1718 1719 if ((attributes->cursor = pw->paned.cursor) != None) 1720 *valueMask |= CWCursor; 1721 1722 (*SuperClass->core_class.realize)(w, valueMask, attributes); 1723 1724 /* 1725 * Before we commit the new locations we need to realize all the panes and 1726 * their grips 1727 */ 1728 ForAllPanes(pw, childP) { 1729 XtRealizeWidget(*childP); 1730 if (HasGrip(*childP)) 1731 XtRealizeWidget(PaneInfo(*childP)->grip); 1732 } 1733 1734 RefigureLocationsAndCommit(w); 1735 pw->paned.resize_children_to_pref = False; 1736} 1737 1738static void 1739XawPanedDestroy(Widget w) 1740{ 1741 ReleaseGCs(w); 1742} 1743 1744static void 1745ReleaseGCs(Widget w) 1746{ 1747 PanedWidget pw = (PanedWidget)w; 1748 1749 XtReleaseGC(w, pw->paned.normgc); 1750 XtReleaseGC(w, pw->paned.invgc); 1751 XtReleaseGC(w, pw->paned.flipgc); 1752} 1753 1754static void 1755XawPanedInsertChild(Widget w) 1756{ 1757 Pane pane = PaneInfo(w); 1758 1759 /* insert the child widget in the composite children list with the 1760 superclass insert_child routine 1761 */ 1762 (*SuperClass->composite_class.insert_child)(w); 1763 1764 if (!IsPane(w)) 1765 return; 1766 1767 if (pane->show_grip == True) { 1768 CreateGrip(w); 1769 if (pane->min == PANED_GRIP_SIZE) 1770 pane->min = PaneSize(pane->grip, IsVert((PanedWidget)XtParent(w))); 1771 } 1772 else { 1773 if (pane->min == PANED_GRIP_SIZE) 1774 pane->min = 1; 1775 pane->grip = NULL; 1776 } 1777 1778 pane->size = 0; 1779 pane->paned_adjusted_me = False; 1780} 1781 1782static void 1783XawPanedDeleteChild(Widget w) 1784{ 1785 /* remove the subwidget info and destroy the grip */ 1786 if (IsPane(w) && HasGrip(w)) 1787 XtDestroyWidget(PaneInfo(w)->grip); 1788 1789 /* delete the child widget in the composite children list with the 1790 superclass delete_child routine 1791 */ 1792 (*SuperClass->composite_class.delete_child)(w); 1793} 1794 1795static void 1796XawPanedChangeManaged(Widget w) 1797{ 1798 PanedWidget pw = (PanedWidget)w; 1799 Boolean vert = IsVert(pw); 1800 Dimension size; 1801 Widget *childP; 1802 1803 if (pw->paned.recursively_called++) 1804 return; 1805 1806 /* 1807 * If the size is zero then set it to the size of the widest or tallest pane 1808 */ 1809 1810 if ((size = PaneSize((Widget)pw, !vert)) == 0) { 1811 size = 1; 1812 ForAllChildren(pw, childP) 1813 if (XtIsManaged(*childP) && (PaneSize(*childP, !vert) > size)) 1814 size = PaneSize(*childP, !vert); 1815 } 1816 1817 ManageAndUnmanageGrips(pw); 1818 pw->paned.recursively_called = False; 1819 ResortChildren(pw); 1820 1821 pw->paned.num_panes = 0; 1822 ForAllChildren(pw, childP) 1823 if (IsPane(*childP)) { 1824 if (XtIsManaged(*childP)) { 1825 Pane pane = PaneInfo(*childP); 1826 1827 if (HasGrip(*childP)) 1828 PaneInfo(pane->grip)->position = pw->paned.num_panes; 1829 pane->position = pw->paned.num_panes; /* TEMPORY -CDP 3/89 */ 1830 pw->paned.num_panes++; 1831 } 1832 else 1833 break; /* This list is already sorted */ 1834 } 1835 1836 SetChildrenPrefSizes((PanedWidget) w, size); 1837 1838 /* 1839 * ForAllPanes can now be used 1840 */ 1841 if (PaneSize((Widget) pw, vert) == 0) 1842 AdjustPanedSize(pw, size, NULL, NULL, NULL); 1843 1844 if (XtIsRealized((Widget)pw)) 1845 RefigureLocationsAndCommit((Widget)pw); 1846} 1847 1848static void 1849XawPanedResize(Widget w) 1850{ 1851 SetChildrenPrefSizes((PanedWidget)w, 1852 PaneSize(w, !IsVert((PanedWidget)w))); 1853 RefigureLocationsAndCommit(w); 1854} 1855 1856/*ARGSUSED*/ 1857static void 1858XawPanedRedisplay(Widget w, XEvent *event, Region region) 1859{ 1860 DrawInternalBorders((PanedWidget)w); 1861} 1862 1863/*ARGSUSED*/ 1864static Boolean 1865XawPanedSetValues(Widget old, Widget request, Widget cnew, 1866 ArgList args, Cardinal *num_args) 1867{ 1868 PanedWidget old_pw = (PanedWidget)old; 1869 PanedWidget new_pw = (PanedWidget)cnew; 1870 Boolean redisplay = False; 1871 1872 if ((old_pw->paned.cursor != new_pw->paned.cursor) && XtIsRealized(cnew)) 1873 XDefineCursor(XtDisplay(cnew), XtWindow(cnew), new_pw->paned.cursor); 1874 1875 if (old_pw->paned.internal_bp != new_pw->paned.internal_bp || 1876 old_pw->core.background_pixel != new_pw->core.background_pixel) { 1877 ReleaseGCs(old); 1878 GetGCs(cnew); 1879 redisplay = True; 1880 } 1881 1882 if (old_pw->paned.grip_cursor != new_pw->paned.grip_cursor || 1883 old_pw->paned.v_grip_cursor != new_pw->paned.v_grip_cursor || 1884 old_pw->paned.h_grip_cursor != new_pw->paned.h_grip_cursor) 1885 ChangeAllGripCursors(new_pw); 1886 1887 if (IsVert(old_pw) != IsVert(new_pw)) { 1888 /* 1889 * We are fooling the paned widget into thinking that is needs to 1890 * fully refigure everything, which is what we want 1891 */ 1892 if (IsVert(new_pw)) 1893 XtWidth(new_pw) = 0; 1894 else 1895 XtHeight(new_pw) = 0; 1896 1897 new_pw->paned.resize_children_to_pref = True; 1898 XawPanedChangeManaged(cnew); /* Seems weird, but does the right thing */ 1899 new_pw->paned.resize_children_to_pref = False; 1900 if (new_pw->paned.grip_cursor == None) 1901 ChangeAllGripCursors(new_pw); 1902 return (True); 1903 } 1904 1905 if (old_pw->paned.internal_bw != new_pw->paned.internal_bw) { 1906 AdjustPanedSize(new_pw, PaneSize(cnew, !IsVert(old_pw)), 1907 NULL, NULL, NULL); 1908 RefigureLocationsAndCommit(cnew); 1909 return (True); /* We have done a full configuration, return */ 1910 } 1911 1912 if (old_pw->paned.grip_indent != new_pw->paned.grip_indent && 1913 XtIsRealized(cnew)) { 1914 CommitNewLocations(new_pw); 1915 redisplay = True; 1916 } 1917 1918 return (redisplay); 1919} 1920 1921/*ARGSUSED*/ 1922static Boolean 1923XawPanedPaneSetValues(Widget old, Widget request, Widget cnew, 1924 ArgList args, Cardinal *num_args) 1925{ 1926 Pane old_pane = PaneInfo(old); 1927 Pane new_pane = PaneInfo(cnew); 1928 Boolean redisplay = False; 1929 1930 /* Check for new min and max */ 1931 if (old_pane->min != new_pane->min || old_pane->max != new_pane->max) 1932 XawPanedSetMinMax(cnew, (int)new_pane->min, (int)new_pane->max); 1933 1934 /* Check for change in XtNshowGrip */ 1935 if (old_pane->show_grip != new_pane->show_grip) { 1936 if (new_pane->show_grip == True) { 1937 CreateGrip(cnew); 1938 if (XtIsRealized(XtParent(cnew))) { 1939 if (XtIsManaged(cnew)) /* if paned is unrealized this will 1940 happen automatically at realize time 1941 */ 1942 XtManageChild(PaneInfo(cnew)->grip); /* manage the grip */ 1943 XtRealizeWidget(PaneInfo(cnew)->grip); /* realize the grip */ 1944 CommitNewLocations((PanedWidget)XtParent(cnew)); 1945 } 1946 } 1947 else if (HasGrip(old)) { 1948 XtDestroyWidget(old_pane->grip); 1949 new_pane->grip = NULL; 1950 redisplay = True; 1951 } 1952 } 1953 1954 return (redisplay); 1955} 1956 1957/* 1958 * Public routines 1959 */ 1960/* 1961 * Function: 1962 * XawPanedSetMinMax 1963 * 1964 * Parameters: 1965 * widget - widget that is a child of the Paned widget 1966 * min - new min and max size for the pane 1967 * max - "" 1968 * 1969 * Description: 1970 * Sets the min and max size for a pane. 1971 */ 1972void 1973XawPanedSetMinMax(Widget widget, int min, int max) 1974{ 1975 Pane pane = PaneInfo(widget); 1976 1977 pane->min = min; 1978 pane->max = max; 1979 RefigureLocationsAndCommit(widget->core.parent); 1980} 1981 1982/* 1983 * Function: 1984 * XawPanedGetMinMax 1985 * 1986 * Parameters: 1987 * widget - widget that is a child of the Paned widget 1988 * min - current min and max size for the pane (return) 1989 * max - "" 1990 * 1991 * Description: 1992 * Gets the min and max size for a pane. 1993 */ 1994void 1995XawPanedGetMinMax(Widget widget, int *min, int *max) 1996{ 1997 Pane pane = PaneInfo(widget); 1998 1999 *min = pane->min; 2000 *max = pane->max; 2001} 2002 2003/* 2004 * Function: 2005 * XawPanedSetRefigureMode 2006 * 2007 * Parameters: 2008 * w - paned widget 2009 * mode - if False then inhibit refigure 2010 * 2011 * Description: 2012 * Allows a flag to be set the will inhibit 2013 * the paned widgets relayout routine. 2014 */ 2015void 2016XawPanedSetRefigureMode(Widget w, 2017#if NeedWidePrototypes 2018 int mode 2019#else 2020 Boolean mode 2021#endif 2022) 2023{ 2024 ((PanedWidget)w)->paned.refiguremode = mode; 2025 RefigureLocationsAndCommit(w); 2026} 2027 2028/* 2029 * Function: 2030 * XawPanedGetNumSub 2031 * 2032 * Parameters: 2033 * w - paned widget 2034 * 2035 * Description: 2036 * Returns the number of panes in the paned widget. 2037 * Returns: 2038 * the number of panes in the paned widget 2039 */ 2040int 2041XawPanedGetNumSub(Widget w) 2042{ 2043 return (((PanedWidget)w)->paned.num_panes); 2044} 2045 2046/* 2047 * Function: 2048 * XawPanedAllowResize 2049 * 2050 * Parameters: 2051 * widget - child of the paned widget 2052 * 2053 * Description: 2054 * Allows a flag to be set that determines if the paned 2055 * widget will allow geometry requests from this child. 2056 */ 2057void 2058XawPanedAllowResize(Widget widget, 2059#if NeedWidePrototypes 2060 int allow_resize 2061#else 2062 Boolean allow_resize 2063#endif 2064) 2065{ 2066 PaneInfo(widget)->allow_resize = allow_resize; 2067} 2068