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/Xaw/Grip.h> 68#include <X11/Xaw/PanedP.h> 69#include <X11/Xaw/XawImP.h> 70#include <X11/Xaw/XawInit.h> 71#include "Private.h" 72 73typedef enum { 74 UpLeftPane = 'U', 75 LowRightPane = 'L', 76 ThisBorderOnly = 'T', 77 AnyPane = 'A' 78} Direction; 79 80#define NO_INDEX -100 81#define IS_GRIP NULL 82 83#define PaneInfo(w) ((Pane)(w)->core.constraints) 84#define HasGrip(w) (PaneInfo(w)->grip != NULL) 85#define IsPane(w) ((w)->core.widget_class != gripWidgetClass) 86#define PaneIndex(w) (PaneInfo(w)->position) 87#define IsVert(w) ((w)->paned.orientation == XtorientVertical) 88 89#define ForAllPanes(pw, childP) \ 90for ((childP) = (pw)->composite.children; \ 91 (childP) < (pw)->composite.children + (pw)->paned.num_panes; \ 92 (childP)++) 93 94#define ForAllChildren(pw, childP) \ 95for ((childP) = (pw)->composite.children; \ 96 (childP) < (pw)->composite.children + (pw)->composite.num_children; \ 97 (childP)++) 98 99#define PaneSize(paned, vertical) \ 100 ((vertical) ? XtHeight(paned) : XtWidth(paned)) 101 102#define GetRequestInfo(geo, vertical) \ 103 ((vertical) ? (geo)->height : (geo)->width) 104 105#define SatisfiesRule1(pane, shrink) \ 106 (((shrink) && ((pane)->size != (pane)->min)) \ 107 || (!(shrink) && ((pane)->size != (pane)->max))) 108 109#define SatisfiesRule2(pane) \ 110 (!(pane)->skip_adjust || (pane)->paned_adjusted_me) 111 112#define SatisfiesRule3(pane, shrink) \ 113 ((pane)->paned_adjusted_me \ 114 && (((shrink) && ((int)(pane)->wp_size <= (pane)->size)) \ 115 || (!(shrink) && ((int)(pane)->wp_size >= (pane)->size)))) 116 117 118/* 119 * Class Methods 120 */ 121static void XawPanedClassInitialize(void); 122static void XawPanedChangeManaged(Widget); 123static void XawPanedDeleteChild(Widget); 124static void XawPanedDestroy(Widget); 125static XtGeometryResult XawPanedGeometryManager(Widget, XtWidgetGeometry*, 126 XtWidgetGeometry*); 127static void XawPanedInitialize(Widget, Widget, ArgList, Cardinal*); 128static void XawPanedInsertChild(Widget); 129static Boolean XawPanedPaneSetValues(Widget, Widget, Widget, 130 ArgList, Cardinal*); 131static void XawPanedRealize(Widget, Mask*, XSetWindowAttributes*); 132static void XawPanedRedisplay(Widget, XEvent*, Region); 133static void XawPanedResize(Widget); 134static Boolean XawPanedSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 135 136/* 137 * Prototypes 138 */ 139static void _DrawInternalBorders(PanedWidget, GC); 140static void _DrawRect(PanedWidget, GC, int, int, unsigned int, unsigned int); 141static void _DrawTrackLines(PanedWidget, Bool); 142static void AdjustPanedSize(PanedWidget, unsigned int, XtGeometryResult*, 143 Dimension*, Dimension*); 144static void ChangeAllGripCursors(PanedWidget); 145static Pane ChoosePaneToResize(PanedWidget, int, Direction, Bool); 146static void ClearPaneStack(PanedWidget); 147static void CommitGripAdjustment(PanedWidget); 148static void CreateGrip(Widget); 149static int GetEventLocation(PanedWidget, XEvent*); 150static void GetGCs(Widget); 151static void GetPaneStack(PanedWidget, Bool, Pane*, int*); 152static void HandleGrip(Widget, XtPointer, XtPointer); 153static void LoopAndRefigureChildren(PanedWidget, int, Direction, int*); 154static void ManageAndUnmanageGrips(PanedWidget); 155static void MoveGripAdjustment(PanedWidget, Widget, Direction, int); 156static Bool PopPaneStack(PanedWidget); 157static void PushPaneStack(PanedWidget, Pane); 158static void RefigureLocations(PanedWidget, int, Direction); 159static void RefigureLocationsAndCommit(Widget); 160static void ReleaseGCs(Widget); 161static void ResortChildren(PanedWidget); 162static void SetChildrenPrefSizes(PanedWidget, unsigned int); 163static void StartGripAdjustment(PanedWidget, Widget, Direction); 164 165/* 166 * Initialization 167 */ 168static char defGripTranslations[] = 169"<Btn1Down>:" "GripAction(Start,UpLeftPane)\n" 170"<Btn2Down>:" "GripAction(Start,ThisBorderOnly)\n" 171"<Btn3Down>:" "GripAction(Start,LowRightPane)\n" 172"<Btn1Motion>:" "GripAction(Move,UpLeft)\n" 173"<Btn2Motion>:" "GripAction(Move,ThisBorder)\n" 174"<Btn3Motion>:" "GripAction(Move,LowRight)\n" 175"Any<BtnUp>:" "GripAction(Commit)\n" 176; 177 178#define offset(field) XtOffsetOf(PanedRec, paned.field) 179static XtResource resources[] = { 180 { 181 XtNinternalBorderColor, 182 XtCBorderColor, 183 XtRPixel, 184 sizeof(Pixel), 185 offset(internal_bp), 186 XtRString, 187 (XtPointer)XtDefaultForeground 188 }, 189 { 190 XtNinternalBorderWidth, 191 XtCBorderWidth, 192 XtRDimension, 193 sizeof(Dimension), 194 offset(internal_bw), 195 XtRImmediate, 196 (XtPointer)1 197 }, 198 { 199 XtNgripIndent, 200 XtCGripIndent, 201 XtRPosition, 202 sizeof(Position), 203 offset(grip_indent), 204 XtRImmediate, 205 (XtPointer)10 206 }, 207 { 208 XtNrefigureMode, 209 XtCBoolean, 210 XtRBoolean, 211 sizeof(Boolean), 212 offset(refiguremode), 213 XtRImmediate, 214 (XtPointer)True 215 }, 216 { 217 XtNgripTranslations, 218 XtCTranslations, 219 XtRTranslationTable, 220 sizeof(XtTranslations), 221 offset(grip_translations), 222 XtRString, 223 (XtPointer)defGripTranslations 224 }, 225 { 226 XtNorientation, 227 XtCOrientation, 228 XtROrientation, 229 sizeof(XtOrientation), 230 offset(orientation), 231 XtRImmediate, 232 (XtPointer)XtorientVertical 233 }, 234 { 235 XtNcursor, 236 XtCCursor, 237 XtRCursor, 238 sizeof(Cursor), 239 offset(cursor), 240 XtRImmediate, 241 NULL 242 }, 243 { 244 XtNgripCursor, 245 XtCCursor, 246 XtRCursor, 247 sizeof(Cursor), 248 offset(grip_cursor), 249 XtRImmediate, 250 NULL 251 }, 252 { 253 XtNverticalGripCursor, 254 XtCCursor, 255 XtRCursor, 256 sizeof(Cursor), 257 offset(v_grip_cursor), 258 XtRString, 259 (XtPointer)"sb_v_double_arrow" 260 }, 261 { 262 XtNhorizontalGripCursor, 263 XtCCursor, 264 XtRCursor, 265 sizeof(Cursor), 266 offset(h_grip_cursor), 267 XtRString, 268 (XtPointer)"sb_h_double_arrow" 269 }, 270 { 271 XtNbetweenCursor, 272 XtCCursor, 273 XtRCursor, 274 sizeof(Cursor), 275 offset(adjust_this_cursor), 276 XtRString, 277 NULL 278 }, 279 { 280 XtNverticalBetweenCursor, 281 XtCCursor, 282 XtRCursor, 283 sizeof(Cursor), 284 offset(v_adjust_this_cursor), 285 XtRString, 286 (XtPointer)"sb_left_arrow" 287 }, 288 { 289 XtNhorizontalBetweenCursor, 290 XtCCursor, 291 XtRCursor, 292 sizeof(Cursor), 293 offset(h_adjust_this_cursor), 294 XtRString, 295 (XtPointer)"sb_up_arrow" 296 }, 297 { 298 XtNupperCursor, 299 XtCCursor, 300 XtRCursor, 301 sizeof(Cursor), 302 offset(adjust_upper_cursor), 303 XtRString, 304 (XtPointer)"sb_up_arrow" 305 }, 306 { 307 XtNlowerCursor, 308 XtCCursor, 309 XtRCursor, 310 sizeof(Cursor), 311 offset(adjust_lower_cursor), 312 XtRString, 313 (XtPointer)"sb_down_arrow" 314 }, 315 { 316 XtNleftCursor, 317 XtCCursor, 318 XtRCursor, 319 sizeof(Cursor), 320 offset(adjust_left_cursor), 321 XtRString, 322 (XtPointer)"sb_left_arrow" 323 }, 324 { 325 XtNrightCursor, 326 XtCCursor, 327 XtRCursor, 328 sizeof(Cursor), 329 offset(adjust_right_cursor), 330 XtRString, 331 (XtPointer)"sb_right_arrow" 332 }, 333}; 334#undef offset 335 336#define offset(field) XtOffsetOf(PanedConstraintsRec, paned.field) 337static XtResource subresources[] = { 338 { 339 XtNallowResize, 340 XtCBoolean, 341 XtRBoolean, 342 sizeof(Boolean), 343 offset(allow_resize), 344 XtRImmediate, 345 (XtPointer)False 346 }, 347 { 348 XtNposition, 349 XtCPosition, 350 XtRInt, 351 sizeof(int), 352 offset(position), 353 XtRImmediate, 354 (XtPointer)0 355 }, 356 { 357 XtNmin, 358 XtCMin, 359 XtRDimension, 360 sizeof(Dimension), 361 offset(min), 362 XtRImmediate, 363 (XtPointer)PANED_GRIP_SIZE 364 }, 365 { 366 XtNmax, 367 XtCMax, 368 XtRDimension, 369 sizeof(Dimension), 370 offset(max), 371 XtRImmediate, 372 (XtPointer)~0 373 }, 374 { 375 XtNpreferredPaneSize, 376 XtCPreferredPaneSize, 377 XtRDimension, 378 sizeof(Dimension), 379 offset(preferred_size), 380 XtRImmediate, 381 (XtPointer)PANED_ASK_CHILD 382 }, 383 { 384 XtNresizeToPreferred, 385 XtCBoolean, 386 XtRBoolean, 387 sizeof(Boolean), 388 offset(resize_to_pref), 389 XtRImmediate, 390 (XtPointer)False 391 }, 392 { 393 XtNskipAdjust, 394 XtCBoolean, 395 XtRBoolean, 396 sizeof(Boolean), 397 offset(skip_adjust), 398 XtRImmediate, 399 (XtPointer)False 400 }, 401 { 402 XtNshowGrip, 403 XtCShowGrip, 404 XtRBoolean, 405 sizeof(Boolean), 406 offset(show_grip), 407 XtRImmediate, 408 (XtPointer)True 409 }, 410}; 411#undef offset 412 413#define SuperClass ((ConstraintWidgetClass)&constraintClassRec) 414 415PanedClassRec panedClassRec = { 416 /* core */ 417 { 418 (WidgetClass)SuperClass, /* superclass */ 419 "Paned", /* class name */ 420 sizeof(PanedRec), /* size */ 421 XawPanedClassInitialize, /* class_initialize */ 422 NULL, /* class_part init */ 423 False, /* class_inited */ 424 XawPanedInitialize, /* initialize */ 425 NULL, /* initialize_hook */ 426 XawPanedRealize, /* realize */ 427 NULL, /* actions */ 428 0, /* num_actions */ 429 resources, /* resources */ 430 XtNumber(resources), /* num_resources */ 431 NULLQUARK, /* xrm_class */ 432 True, /* compress_motion */ 433 True, /* compress_exposure */ 434 True, /* compress_enterleave */ 435 False, /* visible_interest */ 436 XawPanedDestroy, /* destroy */ 437 XawPanedResize, /* resize */ 438 XawPanedRedisplay, /* expose */ 439 XawPanedSetValues, /* set_values */ 440 NULL, /* set_values_hook */ 441 XtInheritSetValuesAlmost, /* set_values_almost */ 442 NULL, /* get_values_hook */ 443 NULL, /* accept_focus */ 444 XtVersion, /* version */ 445 NULL, /* callback_private */ 446 NULL, /* tm_table */ 447 XtInheritQueryGeometry, /* query_geometry */ 448 XtInheritDisplayAccelerator, /* display_accelerator */ 449 NULL, /* extension */ 450 }, 451 /* composite */ 452 { 453 XawPanedGeometryManager, /* geometry_manager */ 454 XawPanedChangeManaged, /* change_managed */ 455 XawPanedInsertChild, /* insert_child */ 456 XawPanedDeleteChild, /* delete_child */ 457 NULL, /* extension */ 458 }, 459 /* constraint */ 460 { 461 subresources, /* subresources */ 462 XtNumber(subresources), /* subresource_count */ 463 sizeof(PanedConstraintsRec), /* constraint_size */ 464 NULL, /* initialize */ 465 NULL, /* destroy */ 466 XawPanedPaneSetValues, /* set_values */ 467 NULL, /* extension */ 468 }, 469 /* paned */ 470 { 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 = (Dimension)(newsize + (size + pw->paned.internal_bw)); 514 } 515 newsize = (Dimension)(newsize - pw->paned.internal_bw); 516 517 if (newsize < 1) 518 newsize = 1; 519 520 if (IsVert(pw)) { 521 request.width = (Dimension)off_size; 522 request.height = newsize; 523 } 524 else { 525 request.width = newsize; 526 request.height = (Dimension)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 = (Dimension)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 preferred 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 = (Dimension) 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 proceeds 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 = (Dimension)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 = (Position)(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 = { .stack_mode = Above }; 800 801 ForAllPanes(pw, childP) { 802 Pane pane = PaneInfo(*childP); 803 Widget grip = pane->grip; /* may be NULL */ 804 805 if (IsVert(pw)) { 806 XtMoveWidget(*childP, (Position) 0, pane->delta); 807 XtResizeWidget(*childP, XtWidth(pw), (Dimension)pane->size, 0); 808 809 if (HasGrip(*childP)) { /* Move and Display the Grip */ 810 changes.x = XtWidth(pw) - pw->paned.grip_indent - 811 XtWidth(grip) - (XtBorderWidth(grip) << 1); 812 changes.y = XtY(*childP) + XtHeight(*childP) - 813 (XtHeight(grip) >> 1) - XtBorderWidth(grip) + 814 (pw->paned.internal_bw >> 1); 815 } 816 } 817 else { 818 XtMoveWidget(*childP, pane->delta, 0); 819 XtResizeWidget(*childP, (Dimension)pane->size, (Dimension)XtHeight(pw), 0); 820 821 if (HasGrip(*childP)) { /* Move and Display the Grip */ 822 changes.x = XtX(*childP) + XtWidth(*childP) - 823 (XtWidth(grip) >> 1) - XtBorderWidth(grip) + 824 (pw->paned.internal_bw >> 1); 825 changes.y = XtHeight(pw) - pw->paned.grip_indent - 826 XtHeight(grip) - (XtBorderWidth(grip) << 1); 827 } 828 } 829 830 /* 831 * This should match XtMoveWidget, except that we're also insuring the 832 * grip is Raised in the same request 833 */ 834 835 if (HasGrip(*childP)) { 836 XtX(grip) = (Position)changes.x; 837 XtY(grip) = (Position)changes.y; 838 839 if (XtIsRealized(pane->grip)) 840 XConfigureWindow(XtDisplay(pane->grip), XtWindow(pane->grip), 841 CWX | CWY | CWStackMode, &changes); 842 } 843 } 844 ClearPaneStack(pw); 845} 846 847/* 848 * Function: 849 * RefigureLocationsAndCommit 850 * 851 * Parameters: 852 * pw - paned widget 853 * 854 * Description: 855 * Refigures all locations in a paned widget and commits them immediately. 856 * 857 * This function does nothing if any of the following are true. 858 * o refiguremode is false. 859 * o The widget is unrealized. 860 * o There are no panes is the paned widget. 861 */ 862static void 863RefigureLocationsAndCommit(Widget w) 864{ 865 PanedWidget pw = (PanedWidget)w; 866 867 if (pw->paned.refiguremode && XtIsRealized(w) && pw->paned.num_panes > 0) { 868 RefigureLocations(pw, NO_INDEX, AnyPane); 869 CommitNewLocations(pw); 870 } 871} 872 873/* 874 * Function: 875 * _DrawRect 876 * 877 * Parameters: 878 * pw - paned widget 879 * gc - gc to used for the draw 880 * on_olc - location of upper left corner of rect 881 * off_loc - "" 882 * on_size - size of rectangle 883 * off_size - "" 884 * 885 * Description: 886 * Draws a rectangle in the proper orientation. 887 */ 888static void 889_DrawRect(PanedWidget pw, GC gc, int on_loc, int off_loc, 890 unsigned int on_size, unsigned int off_size) 891{ 892 if (IsVert(pw)) 893 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 894 off_loc, on_loc, off_size, on_size); 895 else 896 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 897 on_loc, off_loc, on_size, off_size); 898} 899 900/* 901 * Function: 902 * _DrawInternalBorders 903 * 904 * Parameters: 905 * pw - paned widget 906 * gc - GC to use to draw the borders 907 * 908 * Description: 909 * Draws the internal borders into the paned widget. 910 */ 911static void 912_DrawInternalBorders(PanedWidget pw, GC gc) 913{ 914 Widget *childP; 915 int off_loc; 916 unsigned int on_size, off_size; 917 918 /* 919 * This is an optimization. Do not paint the internal borders if 920 * they are the same color as the background 921 */ 922 if (pw->core.background_pixel == pw->paned.internal_bp) 923 return; 924 925 off_loc = 0; 926 off_size = (unsigned int) PaneSize((Widget)pw, !IsVert(pw)); 927 on_size = (unsigned int)pw->paned.internal_bw; 928 929 ForAllPanes(pw, childP) { 930 int on_loc = IsVert(pw) ? XtY(*childP) : XtX(*childP); 931 on_loc -= (int)on_size; 932 933 _DrawRect(pw, gc, on_loc, off_loc, on_size, off_size); 934 } 935} 936 937#define DrawInternalBorders(pw) \ 938 _DrawInternalBorders((pw), (pw)->paned.normgc) 939#define EraseInternalBorders(pw) \ 940 _DrawInternalBorders((pw), (pw)->paned.invgc) 941/* 942 * Function Name: 943 * _DrawTrackLines 944 * 945 * Parameters: 946 * pw - Paned widget 947 * erase - if True then just erase track lines, else draw them in 948 * 949 * Description: 950 * Draws the lines that animate the pane borders when the grips are moved. 951 */ 952static void 953_DrawTrackLines(PanedWidget pw, Bool erase) 954{ 955 Widget *childP; 956 Pane pane; 957 int on_loc, off_loc; 958 unsigned int on_size, off_size; 959 960 off_loc = 0; 961 off_size = PaneSize((Widget)pw, !IsVert(pw)); 962 963 ForAllPanes(pw, childP) { 964 pane = PaneInfo(*childP); 965 if (erase || pane->olddelta != pane->delta) { 966 on_size = pw->paned.internal_bw; 967 if (!erase) { 968 on_loc = PaneInfo(*childP)->olddelta - (int) on_size; 969 _DrawRect(pw, pw->paned.flipgc, 970 on_loc, off_loc, on_size, off_size); 971 } 972 973 on_loc = PaneInfo(*childP)->delta - (int)on_size; 974 975 _DrawRect(pw, pw->paned.flipgc, 976 on_loc, off_loc, on_size, off_size); 977 978 pane->olddelta = pane->delta; 979 } 980 } 981} 982 983#define DrawTrackLines(pw) _DrawTrackLines((pw), False); 984#define EraseTrackLines(pw) _DrawTrackLines((pw), True); 985/* 986 * Function: 987 * GetEventLocation 988 * 989 * Parameters: 990 * pw - the paned widget 991 * event - pointer to an event 992 * 993 * Description: 994 * Converts and event to an x and y location. 995 * 996 * Returns: 997 * if this is a vertical pane then (y) else (x) 998 */ 999static int 1000GetEventLocation(PanedWidget pw, XEvent *event) 1001{ 1002 int x, y; 1003 1004 switch (event->xany.type) { 1005 case ButtonPress: 1006 case ButtonRelease: 1007 x = event->xbutton.x_root; 1008 y = event->xbutton.y_root; 1009 break; 1010 case KeyPress: 1011 case KeyRelease: 1012 x = event->xkey.x_root; 1013 y = event->xkey.y_root; 1014 break; 1015 case MotionNotify: 1016 x = event->xmotion.x_root; 1017 y = event->xmotion.y_root; 1018 break; 1019 default: 1020 x = pw->paned.start_loc; 1021 y = pw->paned.start_loc; 1022 } 1023 1024 if (IsVert(pw)) 1025 return (y); 1026 1027 return (x); 1028} 1029 1030/* 1031 * Function: 1032 * StartGripAdjustment 1033 * 1034 * Parameters: 1035 * pw - paned widget 1036 * grip - grip widget selected 1037 * dir - direction that we are to be moving 1038 * 1039 * Description: 1040 * Starts the grip adjustment procedure. 1041 */ 1042static void 1043StartGripAdjustment(PanedWidget pw, Widget grip, Direction dir) 1044{ 1045 Widget *childP; 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 Cursor cursor; 1059 1060 if (IsVert(pw)) { 1061 if (dir == UpLeftPane) 1062 cursor = pw->paned.adjust_upper_cursor; 1063 else if (dir == LowRightPane) 1064 cursor = pw->paned.adjust_lower_cursor; 1065 else { 1066 if (pw->paned.adjust_this_cursor == None) 1067 cursor = pw->paned.v_adjust_this_cursor; 1068 else 1069 cursor = pw->paned.adjust_this_cursor; 1070 } 1071 } 1072 else { 1073 if (dir == UpLeftPane) 1074 cursor = pw->paned.adjust_left_cursor; 1075 else if (dir == LowRightPane) 1076 cursor = pw->paned.adjust_right_cursor; 1077 else { 1078 if (pw->paned.adjust_this_cursor == None) 1079 cursor = pw->paned.h_adjust_this_cursor; 1080 else 1081 cursor = pw->paned.adjust_this_cursor; 1082 } 1083 } 1084 1085 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1086 } 1087 1088 EraseInternalBorders(pw); 1089 ForAllPanes(pw, childP) 1090 PaneInfo(*childP)->olddelta = -99; 1091 1092 EraseTrackLines(pw); 1093} 1094 1095/* 1096 * Function: 1097 * MoveGripAdjustment 1098 * 1099 * Parameters: 1100 * pw - paned widget 1101 * grip - grip that we are moving 1102 * dir - direction the pane we are interested is w.r.t the grip 1103 * loc - location of pointer in proper direction 1104 * 1105 * Description: 1106 * This routine moves all panes around when a grip is moved. 1107 */ 1108static void 1109MoveGripAdjustment(PanedWidget pw, Widget grip, Direction dir, int loc) 1110{ 1111 int diff, add_size = 0, sub_size = 0; 1112 1113 diff = loc - pw->paned.start_loc; 1114 1115 if (pw->paned.whichadd) 1116 add_size = PaneSize(pw->paned.whichadd, IsVert(pw)) + diff; 1117 1118 if (pw->paned.whichsub) 1119 sub_size = PaneSize(pw->paned.whichsub, IsVert(pw)) - diff; 1120 1121 /* 1122 * If moving this border only then do not allow either of the borders 1123 * to go beyond the min or max size allowed 1124 */ 1125 if (dir == ThisBorderOnly) { 1126 int old_add_size = add_size, old_sub_size; 1127 1128 if (pw->paned.whichadd == NULL) 1129 return; 1130 1131 AssignMax(add_size, (int)PaneInfo(pw->paned.whichadd)->min); 1132 AssignMin(add_size, (int)PaneInfo(pw->paned.whichadd)->max); 1133 if (add_size != old_add_size) 1134 sub_size += old_add_size - add_size; 1135 1136 if (pw->paned.whichsub == NULL) 1137 return; 1138 1139 old_sub_size = sub_size; 1140 AssignMax(sub_size, (int)PaneInfo(pw->paned.whichsub)->min); 1141 AssignMin(sub_size, (int)PaneInfo(pw->paned.whichsub)->max); 1142 if (sub_size != old_sub_size) 1143 return; /* Abort to current sizes */ 1144 } 1145 1146 if (add_size != 0) 1147 PaneInfo(pw->paned.whichadd)->size = add_size; 1148 if (sub_size != 0) 1149 PaneInfo(pw->paned.whichsub)->size = sub_size; 1150 RefigureLocations(pw, PaneIndex(grip), dir); 1151 DrawTrackLines(pw); 1152} 1153 1154/* 1155 * Function: 1156 * CommitGripAdjustment 1157 * 1158 * Parameters: 1159 * pw - paned widget 1160 * 1161 * Description: 1162 * Commits the grip adjustment. 1163 */ 1164static void 1165CommitGripAdjustment(PanedWidget pw) 1166{ 1167 EraseTrackLines(pw); 1168 CommitNewLocations(pw); 1169 DrawInternalBorders(pw); 1170 1171 /* 1172 * Since the user selected this size then use it as the preferred size 1173 */ 1174 if (pw->paned.whichadd) { 1175 Pane pane = PaneInfo(pw->paned.whichadd); 1176 1177 pane->wp_size = (Dimension)pane->size; 1178 } 1179 if (pw->paned.whichsub) { 1180 Pane pane = PaneInfo(pw->paned.whichsub); 1181 1182 pane->wp_size = (Dimension)pane->size; 1183 } 1184} 1185 1186/* 1187 * Function: 1188 * HandleGrip 1189 * 1190 * Parameters: 1191 * grip - grip widget that has been moved 1192 * temp - (not used) 1193 * call_data - data passed to us from the grip widget 1194 * 1195 * Description: 1196 * Handles the grip manipulations. 1197 */ 1198/*ARGSUSED*/ 1199static void 1200HandleGrip(Widget grip, XtPointer temp _X_UNUSED, XtPointer callData) 1201{ 1202 XawGripCallData call_data = (XawGripCallData)callData; 1203 PanedWidget pw = (PanedWidget) XtParent(grip); 1204 int loc; 1205 char action_type[2], direction[2]; 1206 Cursor cursor; 1207 Arg arglist[1]; 1208 1209 if (call_data->num_params) 1210 XmuNCopyISOLatin1Uppered(action_type, call_data->params[0], 1211 sizeof(action_type)); 1212 1213 if (call_data->num_params == 0 1214 || (action_type[0] == 'C' && call_data->num_params != 1) 1215 || (action_type[0] != 'C' && call_data->num_params != 2)) 1216 XtAppError(XtWidgetToApplicationContext(grip), 1217 "Paned GripAction has been passed incorrect parameters."); 1218 1219 loc = GetEventLocation(pw, (XEvent *)call_data->event); 1220 1221 if (action_type[0] != 'C') 1222 XmuNCopyISOLatin1Uppered(direction, call_data->params[1], 1223 sizeof(direction)); 1224 1225 switch (action_type[0]) { 1226 case 'S': /* Start adjustment */ 1227 pw->paned.resize_children_to_pref = False; 1228 StartGripAdjustment(pw, grip, (Direction)direction[0]); 1229 pw->paned.start_loc = loc; 1230 break; 1231 case 'M': 1232 MoveGripAdjustment(pw, grip, (Direction)direction[0], loc); 1233 break; 1234 case 'C': 1235 XtSetArg(arglist[0], XtNcursor, &cursor); 1236 XtGetValues(grip, arglist, 1); 1237 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1238 CommitGripAdjustment(pw); 1239 break; 1240 default: 1241 XtAppError(XtWidgetToApplicationContext(grip), 1242 "Paned GripAction(); 1st parameter invalid"); 1243 break; 1244 } 1245} 1246 1247/* 1248 * Function: 1249 * ResortChildren 1250 * 1251 * Arguments: 1252 * pw - paned widget 1253 * 1254 * Description: 1255 * Resorts the children so that all managed children are first. 1256 */ 1257static void 1258ResortChildren(PanedWidget pw) 1259{ 1260 Widget *unmanagedP, *childP; 1261 1262 unmanagedP = NULL; 1263 ForAllChildren(pw, childP) { 1264 if (!IsPane(*childP) || !XtIsManaged(*childP)) { 1265 /* 1266 * We only keep track of the first unmanaged pane 1267 */ 1268 if (unmanagedP == NULL) 1269 unmanagedP = childP; 1270 } 1271 else { /* must be a managed pane */ 1272 /* 1273 * If an earlier widget was not a managed pane, then swap 1274 */ 1275 if (unmanagedP != NULL) { 1276 Widget child = *unmanagedP; 1277 1278 *unmanagedP = *childP; 1279 *childP = child; 1280 childP = unmanagedP; /* easiest to just back-track */ 1281 unmanagedP = NULL; /* in case there is another managed */ 1282 } 1283 } 1284 } 1285} 1286 1287/* 1288 * Function: 1289 * ManageAndUnmanageGrips 1290 * 1291 * Parameters: 1292 * pw - paned widget 1293 * 1294 * Description: 1295 * This function manages and unmanages the grips so that 1296 * the managed state of each grip matches that of its pane. 1297 */ 1298static void 1299ManageAndUnmanageGrips(PanedWidget pw) 1300{ 1301 WidgetList managed_grips, unmanaged_grips; 1302 Widget *managedP, *unmanagedP, *childP; 1303 Cardinal alloc_size; 1304 1305 alloc_size = (Cardinal)(sizeof(Widget) * (pw->composite.num_children >> 1)); 1306 managedP = managed_grips = (WidgetList)XtMalloc(alloc_size); 1307 unmanagedP = unmanaged_grips = (WidgetList)XtMalloc(alloc_size); 1308 1309 ForAllChildren(pw, childP) 1310 if (IsPane(*childP) && HasGrip(*childP)) { 1311 if (XtIsManaged(*childP)) 1312 *managedP++ = PaneInfo(*childP)->grip; 1313 else 1314 *unmanagedP++ = PaneInfo(*childP)->grip; 1315 } 1316 1317 if (managedP != managed_grips) { 1318 *unmanagedP++ = *--managedP; /* Last grip is never managed */ 1319 XtManageChildren(managed_grips, (Cardinal)(managedP - managed_grips)); 1320 } 1321 1322 if (unmanagedP != unmanaged_grips) 1323 XtUnmanageChildren(unmanaged_grips, (Cardinal)(unmanagedP - unmanaged_grips)); 1324 1325 XtFree((char *)managed_grips); 1326 XtFree((char *)unmanaged_grips); 1327} 1328 1329/* 1330 * Function: 1331 * CreateGrip 1332 * 1333 * Parameters: 1334 * child - child that wants a grip to be created for it 1335 * 1336 * Description: 1337 * Creates a grip widget. 1338 */ 1339static void 1340CreateGrip(Widget child) 1341{ 1342 PanedWidget pw = (PanedWidget)XtParent(child); 1343 Arg arglist[2]; 1344 Cardinal num_args = 0; 1345 Cursor cursor; 1346 1347 XtSetArg(arglist[num_args], XtNtranslations, pw->paned.grip_translations); 1348 num_args++; 1349 if ((cursor = pw->paned.grip_cursor) == None) { 1350 if (IsVert(pw)) 1351 cursor = pw->paned.v_grip_cursor; 1352 else 1353 cursor = pw->paned.h_grip_cursor; 1354 } 1355 1356 XtSetArg(arglist[num_args], XtNcursor, cursor); 1357 num_args++; 1358 PaneInfo(child)->grip = XtCreateWidget("grip", gripWidgetClass, (Widget)pw, 1359 arglist, num_args); 1360 1361 XtAddCallback(PaneInfo(child)->grip, XtNcallback, 1362 HandleGrip, (XtPointer)child); 1363} 1364 1365/* 1366 * Function: 1367 * GetGCs 1368 * 1369 * Parameters: 1370 * w - paned widget 1371 */ 1372static void 1373GetGCs(Widget w) 1374{ 1375 PanedWidget pw = (PanedWidget)w; 1376 XtGCMask valuemask; 1377 XGCValues values; 1378 1379 /* 1380 * Draw pane borders in internal border color 1381 */ 1382 values.foreground = pw->paned.internal_bp; 1383 valuemask = GCForeground; 1384 pw->paned.normgc = XtGetGC(w, valuemask, &values); 1385 1386 /* 1387 * Erase pane borders with background color 1388 */ 1389 values.foreground = pw->core.background_pixel; 1390 valuemask = GCForeground; 1391 pw->paned.invgc = XtGetGC(w, valuemask, &values); 1392 1393 /* 1394 * Draw Track lines (animate pane borders) in 1395 * internal border color ^ bg color 1396 */ 1397 values.function = GXinvert; 1398 values.plane_mask = pw->paned.internal_bp ^ pw->core.background_pixel; 1399 values.subwindow_mode = IncludeInferiors; 1400 valuemask = GCPlaneMask | GCFunction | GCSubwindowMode; 1401 pw->paned.flipgc = XtGetGC(w, valuemask, &values); 1402} 1403 1404/* 1405 * Function: 1406 * SetChildrenPrefSizes 1407 * 1408 * Parameters: 1409 * pw - paned widget 1410 * 1411 * Description: 1412 * Sets the preferred sizes of the children. 1413 */ 1414static void 1415SetChildrenPrefSizes(PanedWidget pw, unsigned int off_size) 1416{ 1417 Widget *childP; 1418 Boolean vert = IsVert(pw); 1419 XtWidgetGeometry request, reply; 1420 1421 ForAllPanes(pw, childP) 1422 if (pw->paned.resize_children_to_pref || PaneInfo(*childP)->size == 0 || 1423 PaneInfo(*childP)->resize_to_pref) { 1424 if (PaneInfo(*childP)->preferred_size != PANED_ASK_CHILD) 1425 PaneInfo(*childP)->wp_size = PaneInfo(*childP)->preferred_size; 1426 else { 1427 if(vert) { 1428 request.request_mode = CWWidth; 1429 request.width = (Dimension) off_size; 1430 } 1431 else { 1432 request.request_mode = CWHeight; 1433 request.height = (Dimension) off_size; 1434 } 1435 1436 if ((XtQueryGeometry(*childP, &request, &reply) 1437 == XtGeometryAlmost) 1438 && (reply.request_mode = (vert ? CWHeight : CWWidth))) 1439 PaneInfo(*childP)->wp_size = GetRequestInfo(&reply, vert); 1440 else 1441 PaneInfo(*childP)->wp_size = PaneSize(*childP, vert); 1442 } 1443 1444 PaneInfo(*childP)->size = PaneInfo(*childP)->wp_size; 1445 } 1446} 1447 1448/* 1449 * Function: 1450 * ChangeAllGripCursors 1451 * 1452 * Parameters: 1453 * pw - paned widget 1454 * 1455 * Description: 1456 * Changes all the grip cursors. 1457 */ 1458static void 1459ChangeAllGripCursors(PanedWidget pw) 1460{ 1461 Widget *childP; 1462 1463 ForAllPanes(pw, childP) { 1464 Cursor cursor; 1465 1466 if ((cursor = pw->paned.grip_cursor) == None) { 1467 if (IsVert(pw)) 1468 cursor = pw->paned.v_grip_cursor; 1469 else 1470 cursor = pw->paned.h_grip_cursor; 1471 } 1472 1473 if (HasGrip(*childP)) { 1474 Arg arglist[1]; 1475 1476 XtSetArg(arglist[0], XtNcursor, cursor); 1477 XtSetValues(PaneInfo(*childP)->grip, arglist, 1); 1478 } 1479 } 1480} 1481 1482/* 1483 * Function: 1484 * PushPaneStack 1485 * 1486 * Parameters: 1487 * pw - paned widget 1488 * pane - pane that we are pushing 1489 * 1490 * Description: 1491 * Pushes a value onto the pane stack. 1492 */ 1493static void 1494PushPaneStack(PanedWidget pw, Pane pane) 1495{ 1496 PaneStack *stack = (PaneStack *)XtMalloc(sizeof(PaneStack)); 1497 1498 stack->next = pw->paned.stack; 1499 stack->pane = pane; 1500 stack->start_size = pane->size; 1501 1502 pw->paned.stack = stack; 1503} 1504 1505/* 1506 * Function: 1507 * GetPaneStack 1508 * 1509 * Parameters: 1510 * pw - paned widget 1511 * shrink - True if we want to shrink this pane, False otherwise 1512 * pane - pane that we are popping (return) 1513 * start_size - size that this pane started at (return) 1514 * 1515 * Description: 1516 * Gets the top value from the pane stack. 1517 */ 1518static void 1519GetPaneStack(PanedWidget pw, Bool shrink, Pane *pane, int *start_size) 1520{ 1521 if (pw->paned.stack == NULL) { 1522 *pane = NULL; 1523 return; 1524 } 1525 1526 *pane = pw->paned.stack->pane; 1527 *start_size = pw->paned.stack->start_size; 1528 1529 if (shrink != ((*pane)->size > *start_size)) 1530 *pane = NULL; 1531} 1532 1533/* 1534 * Function: 1535 * PopPaneStack 1536 * 1537 * Parameters: 1538 * pw - paned widget 1539 * 1540 * Description: 1541 * Pops the top item off the pane stack. 1542 * 1543 * Returns: True if this is not the last element on the stack 1544 */ 1545static Bool 1546PopPaneStack(PanedWidget pw) 1547{ 1548 PaneStack *stack = pw->paned.stack; 1549 1550 if (stack == NULL) 1551 return (False); 1552 1553 pw->paned.stack = stack->next; 1554 XtFree((char *)stack); 1555 1556 if (pw->paned.stack == NULL) 1557 return (False); 1558 1559 return (True); 1560} 1561 1562/* 1563 * Function: 1564 * ClearPaneStack 1565 * 1566 * Parameters: 1567 * pw - paned widget 1568 * 1569 * Description: 1570 * Removes all entries from the pane stack. 1571 */ 1572static void 1573ClearPaneStack(PanedWidget pw) 1574{ 1575 while(PopPaneStack(pw)) 1576 ; 1577} 1578 1579static void 1580XawPanedClassInitialize(void) 1581{ 1582 XawInitializeWidgetSet(); 1583 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, 1584 NULL, 0); 1585 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString, 1586 NULL, 0, XtCacheNone, NULL); 1587} 1588 1589/* The Geometry Manager only allows changes after Realize if 1590 * allow_resize is True in the constraints record. 1591 * 1592 * For vertically paned widgets: 1593 * 1594 * It only allows height changes, but offers the requested height 1595 * as a compromise if both width and height changes were requested. 1596 * 1597 * For horizontal widgets the converse is true. 1598 * As all good Geometry Managers should, we will return No if the 1599 * request will have no effect; i.e. when the requestor is already 1600 * of the desired geometry. 1601 */ 1602static XtGeometryResult 1603XawPanedGeometryManager(Widget w, XtWidgetGeometry *request, 1604 XtWidgetGeometry *reply) 1605{ 1606 PanedWidget pw = (PanedWidget)XtParent(w); 1607 XtGeometryMask mask = request->request_mode; 1608 Dimension old_size, old_wpsize, old_paned_size; 1609 Pane pane = PaneInfo(w); 1610 Boolean vert = IsVert(pw); 1611 Dimension on_size, off_size; 1612 XtGeometryResult result; 1613 Boolean almost = False; 1614 1615 /* 1616 * If any of the following is true, disallow the geometry change 1617 * 1618 * o The paned widget is realized and allow_resize is false for the pane 1619 * o The child did not ask to change the on_size 1620 * o The request is not a width or height request 1621 * o The requested size is the same as the current size 1622 */ 1623 1624 if ((XtIsRealized((Widget)pw) && !pane->allow_resize) 1625 || !(mask & (vert ? CWHeight : CWWidth)) 1626 || (mask & (XtGeometryMask)(~(CWWidth | CWHeight))) 1627 || GetRequestInfo(request, vert) == PaneSize(w, vert)) 1628 return (XtGeometryNo); 1629 1630 old_paned_size = PaneSize((Widget)pw, vert); 1631 old_wpsize = pane->wp_size; 1632 old_size = (Dimension)pane->size; 1633 1634 pane->wp_size = (Dimension)(pane->size = GetRequestInfo(request, vert)); 1635 1636 AdjustPanedSize(pw, PaneSize((Widget)pw, !vert), &result, &on_size, 1637 &off_size); 1638 1639 /* 1640 * Fool the Refigure Locations proc to thinking that we are 1641 * a different on_size 1642 */ 1643 1644 if (result != XtGeometryNo) { 1645 if (vert) 1646 XtHeight(pw) = on_size; 1647 else 1648 XtWidth(pw) = on_size; 1649 } 1650 1651 RefigureLocations(pw, PaneIndex(w), AnyPane); 1652 1653 /* 1654 * Set up reply struct and reset core on_size 1655 */ 1656 if (vert) { 1657 XtHeight(pw) = old_paned_size; 1658 reply->height = (Dimension) pane->size; 1659 reply->width = off_size; 1660 } 1661 else { 1662 XtWidth(pw) = old_paned_size; 1663 reply->height = off_size; 1664 reply->width = (Dimension) pane->size; 1665 } 1666 1667 /* 1668 * IF either of the following is true 1669 * 1670 * o There was a "off_size" request and the new "off_size" is different 1671 * from that requested 1672 * o There was no "off_size" request and the new "off_size" is different 1673 * 1674 * o The "on_size" we will allow is different from that requested 1675 * 1676 * THEN: set almost 1677 */ 1678 if (!((vert ? CWWidth : CWHeight) & mask)) { 1679 if (vert) 1680 request->width = XtWidth(w); 1681 else 1682 request->height = XtHeight(w); 1683 } 1684 1685 almost = GetRequestInfo(request, !vert) != GetRequestInfo(reply, !vert); 1686 almost = (Boolean)(almost | (GetRequestInfo(request, vert) != GetRequestInfo(reply, vert))); 1687 1688 if ((mask & XtCWQueryOnly) || almost) { 1689 pane->wp_size = old_wpsize; 1690 pane->size = old_size; 1691 RefigureLocations(pw, PaneIndex(w), AnyPane); 1692 reply->request_mode = CWWidth | CWHeight; 1693 if (almost) 1694 return (XtGeometryAlmost); 1695 } 1696 else { 1697 AdjustPanedSize(pw, PaneSize((Widget) pw, !vert), NULL, NULL, NULL); 1698 CommitNewLocations(pw); /* layout already refigured */ 1699 } 1700 1701 return (XtGeometryDone); 1702} 1703 1704/*ARGSUSED*/ 1705static void 1706XawPanedInitialize(Widget request _X_UNUSED, Widget cnew, 1707 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1708{ 1709 PanedWidget pw = (PanedWidget)cnew; 1710 1711 GetGCs((Widget)pw); 1712 1713 pw->paned.recursively_called = False; 1714 pw->paned.stack = NULL; 1715 pw->paned.resize_children_to_pref = True; 1716 pw->paned.num_panes = 0; 1717} 1718 1719static void 1720XawPanedRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes) 1721{ 1722 PanedWidget pw = (PanedWidget)w; 1723 Widget *childP; 1724 1725 if ((attributes->cursor = pw->paned.cursor) != None) 1726 *valueMask |= CWCursor; 1727 1728 (*SuperClass->core_class.realize)(w, valueMask, attributes); 1729 1730 /* 1731 * Before we commit the new locations we need to realize all the panes and 1732 * their grips 1733 */ 1734 ForAllPanes(pw, childP) { 1735 XtRealizeWidget(*childP); 1736 if (HasGrip(*childP)) 1737 XtRealizeWidget(PaneInfo(*childP)->grip); 1738 } 1739 1740 RefigureLocationsAndCommit(w); 1741 pw->paned.resize_children_to_pref = False; 1742} 1743 1744static void 1745XawPanedDestroy(Widget w) 1746{ 1747 ReleaseGCs(w); 1748} 1749 1750static void 1751ReleaseGCs(Widget w) 1752{ 1753 PanedWidget pw = (PanedWidget)w; 1754 1755 XtReleaseGC(w, pw->paned.normgc); 1756 XtReleaseGC(w, pw->paned.invgc); 1757 XtReleaseGC(w, pw->paned.flipgc); 1758} 1759 1760static void 1761XawPanedInsertChild(Widget w) 1762{ 1763 Pane pane = PaneInfo(w); 1764 1765 /* insert the child widget in the composite children list with the 1766 superclass insert_child routine 1767 */ 1768 (*SuperClass->composite_class.insert_child)(w); 1769 1770 if (!IsPane(w)) 1771 return; 1772 1773 if (pane->show_grip == True) { 1774 CreateGrip(w); 1775 if (pane->min == PANED_GRIP_SIZE) 1776 pane->min = PaneSize(pane->grip, IsVert((PanedWidget)XtParent(w))); 1777 } 1778 else { 1779 if (pane->min == PANED_GRIP_SIZE) 1780 pane->min = 1; 1781 pane->grip = NULL; 1782 } 1783 1784 pane->size = 0; 1785 pane->paned_adjusted_me = False; 1786} 1787 1788static void 1789XawPanedDeleteChild(Widget w) 1790{ 1791 /* remove the subwidget info and destroy the grip */ 1792 if (IsPane(w) && HasGrip(w)) 1793 XtDestroyWidget(PaneInfo(w)->grip); 1794 1795 /* delete the child widget in the composite children list with the 1796 superclass delete_child routine 1797 */ 1798 (*SuperClass->composite_class.delete_child)(w); 1799} 1800 1801static void 1802XawPanedChangeManaged(Widget w) 1803{ 1804 PanedWidget pw = (PanedWidget)w; 1805 Boolean vert = IsVert(pw); 1806 Dimension size; 1807 Widget *childP; 1808 1809 if (pw->paned.recursively_called++) 1810 return; 1811 1812 /* 1813 * If the size is zero then set it to the size of the widest or tallest pane 1814 */ 1815 1816 if ((size = PaneSize((Widget)pw, !vert)) == 0) { 1817 size = 1; 1818 ForAllChildren(pw, childP) 1819 if (XtIsManaged(*childP) && (PaneSize(*childP, !vert) > size)) 1820 size = PaneSize(*childP, !vert); 1821 } 1822 1823 ManageAndUnmanageGrips(pw); 1824 pw->paned.recursively_called = False; 1825 ResortChildren(pw); 1826 1827 pw->paned.num_panes = 0; 1828 ForAllChildren(pw, childP) 1829 if (IsPane(*childP)) { 1830 if (XtIsManaged(*childP)) { 1831 Pane pane = PaneInfo(*childP); 1832 1833 if (HasGrip(*childP)) 1834 PaneInfo(pane->grip)->position = pw->paned.num_panes; 1835 pane->position = pw->paned.num_panes; /* TEMPORARY -CDP 3/89 */ 1836 pw->paned.num_panes++; 1837 } 1838 else 1839 break; /* This list is already sorted */ 1840 } 1841 1842 SetChildrenPrefSizes((PanedWidget) w, size); 1843 1844 /* 1845 * ForAllPanes can now be used 1846 */ 1847 if (PaneSize((Widget) pw, vert) == 0) 1848 AdjustPanedSize(pw, size, NULL, NULL, NULL); 1849 1850 if (XtIsRealized((Widget)pw)) 1851 RefigureLocationsAndCommit((Widget)pw); 1852} 1853 1854static void 1855XawPanedResize(Widget w) 1856{ 1857 SetChildrenPrefSizes((PanedWidget)w, 1858 PaneSize(w, !IsVert((PanedWidget)w))); 1859 RefigureLocationsAndCommit(w); 1860} 1861 1862/*ARGSUSED*/ 1863static void 1864XawPanedRedisplay(Widget w, XEvent *event _X_UNUSED, Region region _X_UNUSED) 1865{ 1866 DrawInternalBorders((PanedWidget)w); 1867} 1868 1869/*ARGSUSED*/ 1870static Boolean 1871XawPanedSetValues(Widget old, Widget request _X_UNUSED, Widget cnew, 1872 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1873{ 1874 PanedWidget old_pw = (PanedWidget)old; 1875 PanedWidget new_pw = (PanedWidget)cnew; 1876 Boolean redisplay = False; 1877 1878 if ((old_pw->paned.cursor != new_pw->paned.cursor) && XtIsRealized(cnew)) 1879 XDefineCursor(XtDisplay(cnew), XtWindow(cnew), new_pw->paned.cursor); 1880 1881 if (old_pw->paned.internal_bp != new_pw->paned.internal_bp || 1882 old_pw->core.background_pixel != new_pw->core.background_pixel) { 1883 ReleaseGCs(old); 1884 GetGCs(cnew); 1885 redisplay = True; 1886 } 1887 1888 if (old_pw->paned.grip_cursor != new_pw->paned.grip_cursor || 1889 old_pw->paned.v_grip_cursor != new_pw->paned.v_grip_cursor || 1890 old_pw->paned.h_grip_cursor != new_pw->paned.h_grip_cursor) 1891 ChangeAllGripCursors(new_pw); 1892 1893 if (IsVert(old_pw) != IsVert(new_pw)) { 1894 /* 1895 * We are fooling the paned widget into thinking that is needs to 1896 * fully refigure everything, which is what we want 1897 */ 1898 if (IsVert(new_pw)) 1899 XtWidth(new_pw) = 0; 1900 else 1901 XtHeight(new_pw) = 0; 1902 1903 new_pw->paned.resize_children_to_pref = True; 1904 XawPanedChangeManaged(cnew); /* Seems weird, but does the right thing */ 1905 new_pw->paned.resize_children_to_pref = False; 1906 if (new_pw->paned.grip_cursor == None) 1907 ChangeAllGripCursors(new_pw); 1908 return (True); 1909 } 1910 1911 if (old_pw->paned.internal_bw != new_pw->paned.internal_bw) { 1912 AdjustPanedSize(new_pw, PaneSize(cnew, !IsVert(old_pw)), 1913 NULL, NULL, NULL); 1914 RefigureLocationsAndCommit(cnew); 1915 return (True); /* We have done a full configuration, return */ 1916 } 1917 1918 if (old_pw->paned.grip_indent != new_pw->paned.grip_indent && 1919 XtIsRealized(cnew)) { 1920 CommitNewLocations(new_pw); 1921 redisplay = True; 1922 } 1923 1924 return (redisplay); 1925} 1926 1927/*ARGSUSED*/ 1928static Boolean 1929XawPanedPaneSetValues(Widget old, Widget request _X_UNUSED, Widget cnew, 1930 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1931{ 1932 Pane old_pane = PaneInfo(old); 1933 Pane new_pane = PaneInfo(cnew); 1934 Boolean redisplay = False; 1935 1936 /* Check for new min and max */ 1937 if (old_pane->min != new_pane->min || old_pane->max != new_pane->max) 1938 XawPanedSetMinMax(cnew, (int)new_pane->min, (int)new_pane->max); 1939 1940 /* Check for change in XtNshowGrip */ 1941 if (old_pane->show_grip != new_pane->show_grip) { 1942 if (new_pane->show_grip == True) { 1943 CreateGrip(cnew); 1944 if (XtIsRealized(XtParent(cnew))) { 1945 if (XtIsManaged(cnew)) /* if paned is unrealized this will 1946 happen automatically at realize time 1947 */ 1948 XtManageChild(PaneInfo(cnew)->grip); /* manage the grip */ 1949 XtRealizeWidget(PaneInfo(cnew)->grip); /* realize the grip */ 1950 CommitNewLocations((PanedWidget)XtParent(cnew)); 1951 } 1952 } 1953 else if (HasGrip(old)) { 1954 XtDestroyWidget(old_pane->grip); 1955 new_pane->grip = NULL; 1956 redisplay = True; 1957 } 1958 } 1959 1960 return (redisplay); 1961} 1962 1963/* 1964 * Public routines 1965 */ 1966/* 1967 * Function: 1968 * XawPanedSetMinMax 1969 * 1970 * Parameters: 1971 * widget - widget that is a child of the Paned widget 1972 * min - new min and max size for the pane 1973 * max - "" 1974 * 1975 * Description: 1976 * Sets the min and max size for a pane. 1977 */ 1978void 1979XawPanedSetMinMax(Widget widget, int min, int max) 1980{ 1981 Pane pane = PaneInfo(widget); 1982 1983 pane->min = (Dimension) min; 1984 pane->max = (Dimension) max; 1985 RefigureLocationsAndCommit(widget->core.parent); 1986} 1987 1988/* 1989 * Function: 1990 * XawPanedGetMinMax 1991 * 1992 * Parameters: 1993 * widget - widget that is a child of the Paned widget 1994 * min - current min and max size for the pane (return) 1995 * max - "" 1996 * 1997 * Description: 1998 * Gets the min and max size for a pane. 1999 */ 2000void 2001XawPanedGetMinMax(Widget widget, int *min, int *max) 2002{ 2003 Pane pane = PaneInfo(widget); 2004 2005 *min = pane->min; 2006 *max = pane->max; 2007} 2008 2009/* 2010 * Function: 2011 * XawPanedSetRefigureMode 2012 * 2013 * Parameters: 2014 * w - paned widget 2015 * mode - if False then inhibit refigure 2016 * 2017 * Description: 2018 * Allows a flag to be set the will inhibit 2019 * the paned widgets relayout routine. 2020 */ 2021void 2022XawPanedSetRefigureMode(Widget w, 2023#if NeedWidePrototypes 2024 int mode 2025#else 2026 Boolean mode 2027#endif 2028) 2029{ 2030 ((PanedWidget)w)->paned.refiguremode = mode; 2031 RefigureLocationsAndCommit(w); 2032} 2033 2034/* 2035 * Function: 2036 * XawPanedGetNumSub 2037 * 2038 * Parameters: 2039 * w - paned widget 2040 * 2041 * Description: 2042 * Returns the number of panes in the paned widget. 2043 * Returns: 2044 * the number of panes in the paned widget 2045 */ 2046int 2047XawPanedGetNumSub(Widget w) 2048{ 2049 return (((PanedWidget)w)->paned.num_panes); 2050} 2051 2052/* 2053 * Function: 2054 * XawPanedAllowResize 2055 * 2056 * Parameters: 2057 * widget - child of the paned widget 2058 * 2059 * Description: 2060 * Allows a flag to be set that determines if the paned 2061 * widget will allow geometry requests from this child. 2062 */ 2063void 2064XawPanedAllowResize(Widget widget, 2065#if NeedWidePrototypes 2066 int allow_resize 2067#else 2068 Boolean allow_resize 2069#endif 2070) 2071{ 2072 PaneInfo(widget)->allow_resize = allow_resize; 2073} 2074