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