Paned.c revision 5b16253f
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}; 470 471WidgetClass panedWidgetClass = (WidgetClass)&panedClassRec; 472WidgetClass vPanedWidgetClass = (WidgetClass)&panedClassRec; 473 474/* 475 * Implementation 476 */ 477/* Function: 478 * AdjustPanedSize 479 * 480 * Parameters: 481 * pw - paned widget to adjust 482 * off_size - new off_size to use 483 * result_ret - result of query (return) 484 * on_size_ret - new on_size (return) 485 * off_size_ret - new off_size (return) 486 * 487 * Description: 488 * Adjusts the size of the pane. 489 * 490 * Returns: 491 * amount of change in size 492 */ 493static void 494AdjustPanedSize(PanedWidget pw, unsigned int off_size, 495 XtGeometryResult *result_ret, 496 Dimension *on_size_ret, Dimension *off_size_ret) 497{ 498 Dimension old_size = PaneSize((Widget)pw, IsVert(pw)); 499 Dimension newsize = 0; 500 Widget *childP; 501 XtWidgetGeometry request, reply; 502 503 request.request_mode = CWWidth | CWHeight; 504 505 ForAllPanes(pw, childP) { 506 int size = Max(PaneInfo(*childP)->size, (int)PaneInfo(*childP)->min); 507 508 AssignMin(size, (int)PaneInfo(*childP)->max); 509 newsize = (newsize + (size + pw->paned.internal_bw)); 510 } 511 newsize = (Dimension)(newsize - pw->paned.internal_bw); 512 513 if (newsize < 1) 514 newsize = 1; 515 516 if (IsVert(pw)) { 517 request.width = (Dimension)off_size; 518 request.height = newsize; 519 } 520 else { 521 request.width = newsize; 522 request.height = (Dimension)off_size; 523 } 524 525 if (result_ret != NULL) { 526 request.request_mode |= XtCWQueryOnly; 527 528 *result_ret = XtMakeGeometryRequest((Widget)pw, &request, &reply); 529 _XawImCallVendorShellExtResize((Widget)pw); 530 531 if (newsize == old_size || *result_ret == XtGeometryNo) { 532 *on_size_ret = old_size; 533 *off_size_ret = (Dimension)off_size; 534 return; 535 } 536 if (*result_ret != XtGeometryAlmost) { 537 *on_size_ret = GetRequestInfo(&request, IsVert(pw)); 538 *off_size_ret = GetRequestInfo(&request, !IsVert(pw)); 539 return; 540 } 541 *on_size_ret = GetRequestInfo(&reply, IsVert(pw)); 542 *off_size_ret = GetRequestInfo(&reply, !IsVert(pw)); 543 return; 544 } 545 546 if (newsize == old_size) 547 return; 548 549 if (XtMakeGeometryRequest((Widget)pw, &request, &reply) == XtGeometryAlmost) 550 XtMakeGeometryRequest((Widget)pw, &reply, &request); 551} 552 553/* 554 * Function: 555 * ChoosePaneToResize. 556 * 557 * Parameters: 558 * pw - paned widget 559 * paneindex - index of the current pane 560 * dir - direction to search first 561 * shrink - True if we need to shrink a pane, False otherwise 562 * 563 * Description: 564 * This function chooses a pane to resize. 565 * They are chosen using the following rules: 566 * 567 * 1) size < max && size > min 568 * 2) skip adjust == False 569 * 3) widget not its preferred height 570 * && this change will bring it closer 571 * && The user has not resized this pane. 572 * 573 * If no widgets are found that fits all the rules then 574 * rule #3 is broken. 575 * If there are still no widgets found than 576 * rule #2 is broken. 577 * Rule #1 is never broken. 578 * If no widgets are found then NULL is returned. 579 * 580 * Returns: 581 * pane to resize or NULL 582 */ 583static Pane 584ChoosePaneToResize(PanedWidget pw, int paneindex, Direction dir, Bool shrink) 585{ 586 Widget *childP; 587 int rules = 3; 588 Direction _dir = dir; 589 int _index = paneindex; 590 591 if (paneindex == NO_INDEX || dir == AnyPane) { /* Use defaults */ 592 _dir = LowRightPane; /* Go up - really */ 593 _index = pw->paned.num_panes - 1; /* Start the last pane, and work 594 backwards */ 595 } 596 childP = pw->composite.children + _index; 597 598 /*CONSTCOND*/ 599 while(True) { 600 Pane pane = PaneInfo(*childP); 601 602 if ((rules < 3 || SatisfiesRule3(pane, shrink)) 603 && (rules < 2 || SatisfiesRule2(pane)) 604 && SatisfiesRule1(pane, shrink) 605 && (paneindex != PaneIndex(*childP) || dir == AnyPane)) 606 return (pane); 607 608 /* 609 * This is counter-intuitive, but if we are resizing the pane 610 * above the grip we want to choose a pane below the grip to lose, 611 * and visa-versa 612 */ 613 if (_dir == LowRightPane) 614 --childP; 615 else 616 ++childP; 617 618 /* 619 * If we have come to and edge then reduce the rule set, and try again 620 * If we are reduced the rules to none, then return NULL 621 */ 622 if ((childP - pw->composite.children) < 0 || 623 (childP - pw->composite.children) >= pw->paned.num_panes) { 624 if (--rules < 1) /* less strict rules */ 625 return (NULL); 626 childP = pw->composite.children + _index; 627 } 628 } 629} 630 631/* 632 * Function: 633 * LoopAndRefigureChildren 634 * 635 * Parameters: 636 * pw - paned widget 637 * paneindex - number of the pane border we are moving 638 * dir - pane to move (either UpLeftPane or LowRightPane) 639 * sizeused - current amount of space used (used and returned) 640 * 641 * Description: 642 * If we are resizing either the UpleftPane or LowRight Pane loop 643 * through all the children to see if any will allow us to resize them. 644 */ 645static void 646LoopAndRefigureChildren(PanedWidget pw, int paneindex, Direction dir, 647 int *sizeused) 648{ 649 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw)); 650 Boolean shrink = (*sizeused > pane_size); 651 652 if (dir == LowRightPane) 653 paneindex++; 654 655 /* While all panes do not fit properly */ 656 while (*sizeused != pane_size) { 657 /* 658 * Choose a pane to resize 659 * First look on the Pane Stack, and then go hunting for another one 660 * If we fail to find a pane to resize then give up 661 */ 662 Pane pane; 663 int start_size; 664 Dimension old; 665 Boolean rule3_ok = False, from_stack = True; 666 667 GetPaneStack(pw, shrink, &pane, &start_size); 668 if (pane == NULL) { 669 pane = ChoosePaneToResize(pw, paneindex, dir, shrink); 670 if (pane == NULL) 671 return; /* no one to resize, give up */ 672 673 rule3_ok = SatisfiesRule3(pane, shrink); 674 from_stack = False; 675 PushPaneStack(pw, pane); 676 } 677 678 /* 679 * Try to resize this pane so that all panes will fit, take min and max 680 * into account 681 */ 682 old = (Dimension) pane->size; 683 pane->size += pane_size - *sizeused; 684 685 if (from_stack) { 686 if (shrink) { 687 AssignMax(pane->size, start_size); 688 } /* don't remove these braces */ 689 else 690 AssignMin(pane->size, start_size); 691 692 if (pane->size == start_size) 693 (void)PopPaneStack(pw); 694 } 695 else if (rule3_ok) { 696 if (shrink) { 697 AssignMax(pane->size, (int)pane->wp_size); 698 } /* don't remove these braces */ 699 else 700 AssignMin(pane->size, (int)pane->wp_size); 701 } 702 703 pane->paned_adjusted_me = pane->size != pane->wp_size; 704 AssignMax(pane->size, (int)pane->min); 705 AssignMin(pane->size, (int)pane->max); 706 *sizeused += (pane->size - old); 707 } 708} 709 710/* 711 * Function: 712 * RefigureLocations 713 * 714 * Parameters: 715 * pw - paned widget 716 * paneindex - child to start refiguring at 717 * dir - direction to move from child 718 * 719 * Description: 720 * Refigures all locations of children. 721 * There are special arguments to paneindex and dir, they are: 722 * paneindex - NO_INDEX. 723 * dir - AnyPane. 724 * 725 * If either of these is true then all panes may be resized and 726 * the choosing of panes proceeds in reverse order starting with the 727 * last child. 728 */ 729static void 730RefigureLocations(PanedWidget pw, int paneindex, Direction dir) 731{ 732 Widget *childP; 733 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw)); 734 int sizeused = 0; 735 Position loc = 0; 736 737 if (pw->paned.num_panes == 0 || !pw->paned.refiguremode) 738 return; 739 740 /* 741 * Get an initial estimate of the size we will use 742 */ 743 ForAllPanes(pw, childP) { 744 Pane pane = PaneInfo(*childP); 745 746 AssignMax(pane->size, (int) pane->min); 747 AssignMin(pane->size, (int) pane->max); 748 sizeused += (int)pane->size + (int)pw->paned.internal_bw; 749 } 750 sizeused -= (int)pw->paned.internal_bw; 751 752 if (dir != ThisBorderOnly && sizeused != pane_size) 753 LoopAndRefigureChildren(pw, paneindex, dir, &sizeused); 754 755 /* 756 * If we still are not the right size, then tell the pane that 757 * wanted to resize that it can't 758 */ 759 if (paneindex != NO_INDEX && dir != AnyPane) { 760 Pane pane = PaneInfo(*(pw->composite.children + paneindex)); 761 Dimension old = (Dimension)pane->size; 762 763 pane->size += pane_size - sizeused; 764 AssignMax(pane->size, (int) pane->min); 765 AssignMin(pane->size, (int) pane->max); 766 sizeused += pane->size - old; 767 } 768 769 /* 770 * It is possible that the panes will not fit inside the vpaned widget, but 771 * we have tried out best 772 * 773 * Assign each pane a location 774 */ 775 ForAllPanes(pw, childP) { 776 PaneInfo(*childP)->delta = loc; 777 loc = (Position)(loc + (PaneInfo(*childP)->size + pw->paned.internal_bw)); 778 } 779} 780 781/* 782 * Function: 783 * CommitNewLocations 784 * 785 * Parameters: 786 * pw - paned widget 787 * 788 * Description: 789 * Commits all of the previously figured locations. 790 */ 791static void 792CommitNewLocations(PanedWidget pw) 793{ 794 Widget *childP; 795 XWindowChanges changes; 796 797 changes.stack_mode = Above; 798 799 ForAllPanes(pw, childP) { 800 Pane pane = PaneInfo(*childP); 801 Widget grip = pane->grip; /* may be NULL */ 802 803 if (IsVert(pw)) { 804 XtMoveWidget(*childP, (Position) 0, pane->delta); 805 XtResizeWidget(*childP, XtWidth(pw), (Dimension)pane->size, 0); 806 807 if (HasGrip(*childP)) { /* Move and Display the Grip */ 808 changes.x = XtWidth(pw) - pw->paned.grip_indent - 809 XtWidth(grip) - (XtBorderWidth(grip) << 1); 810 changes.y = XtY(*childP) + XtHeight(*childP) - 811 (XtHeight(grip) >> 1) - XtBorderWidth(grip) + 812 (pw->paned.internal_bw >> 1); 813 } 814 } 815 else { 816 XtMoveWidget(*childP, pane->delta, 0); 817 XtResizeWidget(*childP, (Dimension)pane->size, (Dimension)XtHeight(pw), 0); 818 819 if (HasGrip(*childP)) { /* Move and Display the Grip */ 820 changes.x = XtX(*childP) + XtWidth(*childP) - 821 (XtWidth(grip) >> 1) - XtBorderWidth(grip) + 822 (pw->paned.internal_bw >> 1); 823 changes.y = XtHeight(pw) - pw->paned.grip_indent - 824 XtHeight(grip) - (XtBorderWidth(grip) << 1); 825 } 826 } 827 828 /* 829 * This should match XtMoveWidget, except that we're also insuring the 830 * grip is Raised in the same request 831 */ 832 833 if (HasGrip(*childP)) { 834 XtX(grip) = (Position)changes.x; 835 XtY(grip) = (Position)changes.y; 836 837 if (XtIsRealized(pane->grip)) 838 XConfigureWindow(XtDisplay(pane->grip), XtWindow(pane->grip), 839 CWX | CWY | CWStackMode, &changes); 840 } 841 } 842 ClearPaneStack(pw); 843} 844 845/* 846 * Function: 847 * RefigureLocationsAndCommit 848 * 849 * Parameters: 850 * pw - paned widget 851 * 852 * Description: 853 * Refigures all locations in a paned widget and commits them immediately. 854 * 855 * This function does nothing if any of the following are true. 856 * o refiguremode is false. 857 * o The widget is unrealized. 858 * o There are no panes is the paned widget. 859 */ 860static void 861RefigureLocationsAndCommit(Widget w) 862{ 863 PanedWidget pw = (PanedWidget)w; 864 865 if (pw->paned.refiguremode && XtIsRealized(w) && pw->paned.num_panes > 0) { 866 RefigureLocations(pw, NO_INDEX, AnyPane); 867 CommitNewLocations(pw); 868 } 869} 870 871/* 872 * Function: 873 * _DrawRect 874 * 875 * Parameters: 876 * pw - paned widget 877 * gc - gc to used for the draw 878 * on_olc - location of upper left corner of rect 879 * off_loc - "" 880 * on_size - size of rectangle 881 * off_size - "" 882 * 883 * Description: 884 * Draws a rectangle in the proper orientation. 885 */ 886static void 887_DrawRect(PanedWidget pw, GC gc, int on_loc, int off_loc, 888 unsigned int on_size, unsigned int off_size) 889{ 890 if (IsVert(pw)) 891 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 892 off_loc, on_loc, off_size, on_size); 893 else 894 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc, 895 on_loc, off_loc, on_size, off_size); 896} 897 898/* 899 * Function: 900 * _DrawInternalBorders 901 * 902 * Parameters: 903 * pw - paned widget 904 * gc - GC to use to draw the borders 905 * 906 * Description: 907 * Draws the internal borders into the paned widget. 908 */ 909static void 910_DrawInternalBorders(PanedWidget pw, GC gc) 911{ 912 Widget *childP; 913 int on_loc, off_loc; 914 unsigned int on_size, off_size; 915 916 /* 917 * This is an optimization. Do not paint the internal borders if 918 * they are the same color as the background 919 */ 920 if (pw->core.background_pixel == pw->paned.internal_bp) 921 return; 922 923 off_loc = 0; 924 off_size = (unsigned int) PaneSize((Widget)pw, !IsVert(pw)); 925 on_size = (unsigned int)pw->paned.internal_bw; 926 927 ForAllPanes(pw, childP) { 928 on_loc = IsVert(pw) ? XtY(*childP) : XtX(*childP); 929 on_loc -= (int)on_size; 930 931 _DrawRect(pw, gc, on_loc, off_loc, on_size, off_size); 932 } 933} 934 935#define DrawInternalBorders(pw) \ 936 _DrawInternalBorders((pw), (pw)->paned.normgc) 937#define EraseInternalBorders(pw) \ 938 _DrawInternalBorders((pw), (pw)->paned.invgc) 939/* 940 * Function Name: 941 * _DrawTrackLines 942 * 943 * Parameters: 944 * pw - Paned widget 945 * erase - if True then just erase track lines, else draw them in 946 * 947 * Description: 948 * Draws the lines that animate the pane borders when the grips are moved. 949 */ 950static void 951_DrawTrackLines(PanedWidget pw, Bool erase) 952{ 953 Widget *childP; 954 Pane pane; 955 int on_loc, off_loc; 956 unsigned int on_size, off_size; 957 958 off_loc = 0; 959 off_size = PaneSize((Widget)pw, !IsVert(pw)); 960 961 ForAllPanes(pw, childP) { 962 pane = PaneInfo(*childP); 963 if (erase || pane->olddelta != pane->delta) { 964 on_size = pw->paned.internal_bw; 965 if (!erase) { 966 on_loc = PaneInfo(*childP)->olddelta - (int) on_size; 967 _DrawRect(pw, pw->paned.flipgc, 968 on_loc, off_loc, on_size, off_size); 969 } 970 971 on_loc = PaneInfo(*childP)->delta - (int)on_size; 972 973 _DrawRect(pw, pw->paned.flipgc, 974 on_loc, off_loc, on_size, off_size); 975 976 pane->olddelta = pane->delta; 977 } 978 } 979} 980 981#define DrawTrackLines(pw) _DrawTrackLines((pw), False); 982#define EraseTrackLines(pw) _DrawTrackLines((pw), True); 983/* 984 * Function: 985 * GetEventLocation 986 * 987 * Parameters: 988 * pw - the paned widget 989 * event - pointer to an event 990 * 991 * Description: 992 * Converts and event to an x and y location. 993 * 994 * Returns: 995 * if this is a vertical pane then (y) else (x) 996 */ 997static int 998GetEventLocation(PanedWidget pw, XEvent *event) 999{ 1000 int x, y; 1001 1002 switch (event->xany.type) { 1003 case ButtonPress: 1004 case ButtonRelease: 1005 x = event->xbutton.x_root; 1006 y = event->xbutton.y_root; 1007 break; 1008 case KeyPress: 1009 case KeyRelease: 1010 x = event->xkey.x_root; 1011 y = event->xkey.y_root; 1012 break; 1013 case MotionNotify: 1014 x = event->xmotion.x_root; 1015 y = event->xmotion.y_root; 1016 break; 1017 default: 1018 x = pw->paned.start_loc; 1019 y = pw->paned.start_loc; 1020 } 1021 1022 if (IsVert(pw)) 1023 return (y); 1024 1025 return (x); 1026} 1027 1028/* 1029 * Function: 1030 * StartGripAdjustment 1031 * 1032 * Parameters: 1033 * pw - paned widget 1034 * grip - grip widget selected 1035 * dir - direction that we are to be moving 1036 * 1037 * Description: 1038 * Starts the grip adjustment procedure. 1039 */ 1040static void 1041StartGripAdjustment(PanedWidget pw, Widget grip, Direction dir) 1042{ 1043 Widget *childP; 1044 Cursor cursor; 1045 1046 pw->paned.whichadd = pw->paned.whichsub = NULL; 1047 1048 if (dir == ThisBorderOnly || dir == UpLeftPane) 1049 pw->paned.whichadd = pw->composite.children[PaneIndex(grip)]; 1050 if (dir == ThisBorderOnly || dir == LowRightPane) 1051 pw->paned.whichsub = pw->composite.children[PaneIndex(grip) + 1]; 1052 1053 /* 1054 * Change the cursor 1055 */ 1056 if (XtIsRealized(grip)) { 1057 if (IsVert(pw)) { 1058 if (dir == UpLeftPane) 1059 cursor = pw->paned.adjust_upper_cursor; 1060 else if (dir == LowRightPane) 1061 cursor = pw->paned.adjust_lower_cursor; 1062 else { 1063 if (pw->paned.adjust_this_cursor == None) 1064 cursor = pw->paned.v_adjust_this_cursor; 1065 else 1066 cursor = pw->paned.adjust_this_cursor; 1067 } 1068 } 1069 else { 1070 if (dir == UpLeftPane) 1071 cursor = pw->paned.adjust_left_cursor; 1072 else if (dir == LowRightPane) 1073 cursor = pw->paned.adjust_right_cursor; 1074 else { 1075 if (pw->paned.adjust_this_cursor == None) 1076 cursor = pw->paned.h_adjust_this_cursor; 1077 else 1078 cursor = pw->paned.adjust_this_cursor; 1079 } 1080 } 1081 1082 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1083 } 1084 1085 EraseInternalBorders(pw); 1086 ForAllPanes(pw, childP) 1087 PaneInfo(*childP)->olddelta = -99; 1088 1089 EraseTrackLines(pw); 1090} 1091 1092/* 1093 * Function: 1094 * MoveGripAdjustment 1095 * 1096 * Parameters: 1097 * pw - paned widget 1098 * grip - grip that we are moving 1099 * dir - direction the pane we are interested is w.r.t the grip 1100 * loc - location of pointer in proper direction 1101 * 1102 * Description: 1103 * This routine moves all panes around when a grip is moved. 1104 */ 1105static void 1106MoveGripAdjustment(PanedWidget pw, Widget grip, Direction dir, int loc) 1107{ 1108 int diff, add_size = 0, sub_size = 0; 1109 1110 diff = loc - pw->paned.start_loc; 1111 1112 if (pw->paned.whichadd) 1113 add_size = PaneSize(pw->paned.whichadd, IsVert(pw)) + diff; 1114 1115 if (pw->paned.whichsub) 1116 sub_size = PaneSize(pw->paned.whichsub, IsVert(pw)) - diff; 1117 1118 /* 1119 * If moving this border only then do not allow either of the borders 1120 * to go beyond the min or max size allowed 1121 */ 1122 if (dir == ThisBorderOnly) { 1123 int old_add_size = add_size, old_sub_size; 1124 1125 AssignMax(add_size, (int)PaneInfo(pw->paned.whichadd)->min); 1126 AssignMin(add_size, (int)PaneInfo(pw->paned.whichadd)->max); 1127 if (add_size != old_add_size) 1128 sub_size += old_add_size - add_size; 1129 1130 old_sub_size = sub_size; 1131 AssignMax(sub_size, (int)PaneInfo(pw->paned.whichsub)->min); 1132 AssignMin(sub_size, (int)PaneInfo(pw->paned.whichsub)->max); 1133 if (sub_size != old_sub_size) 1134 return; /* Abort to current sizes */ 1135 } 1136 1137 if (add_size != 0) 1138 PaneInfo(pw->paned.whichadd)->size = add_size; 1139 if (sub_size != 0) 1140 PaneInfo(pw->paned.whichsub)->size = sub_size; 1141 RefigureLocations(pw, PaneIndex(grip), dir); 1142 DrawTrackLines(pw); 1143} 1144 1145/* 1146 * Function: 1147 * CommitGripAdjustment 1148 * 1149 * Parameters: 1150 * pw - paned widget 1151 * 1152 * Description: 1153 * Commits the grip adjustment. 1154 */ 1155static void 1156CommitGripAdjustment(PanedWidget pw) 1157{ 1158 EraseTrackLines(pw); 1159 CommitNewLocations(pw); 1160 DrawInternalBorders(pw); 1161 1162 /* 1163 * Since the user selected this size then use it as the preferred size 1164 */ 1165 if (pw->paned.whichadd) { 1166 Pane pane = PaneInfo(pw->paned.whichadd); 1167 1168 pane->wp_size = (Dimension)pane->size; 1169 } 1170 if (pw->paned.whichsub) { 1171 Pane pane = PaneInfo(pw->paned.whichsub); 1172 1173 pane->wp_size = (Dimension)pane->size; 1174 } 1175} 1176 1177/* 1178 * Function: 1179 * HandleGrip 1180 * 1181 * Parameters: 1182 * grip - grip widget that has been moved 1183 * temp - (not used) 1184 * call_data - data passed to us from the grip widget 1185 * 1186 * Description: 1187 * Handles the grip manipulations. 1188 */ 1189/*ARGSUSED*/ 1190static void 1191HandleGrip(Widget grip, XtPointer temp _X_UNUSED, XtPointer callData) 1192{ 1193 XawGripCallData call_data = (XawGripCallData)callData; 1194 PanedWidget pw = (PanedWidget) XtParent(grip); 1195 int loc; 1196 char action_type[2], direction[2]; 1197 Cursor cursor; 1198 Arg arglist[1]; 1199 1200 if (call_data->num_params) 1201 XmuNCopyISOLatin1Uppered(action_type, call_data->params[0], 1202 sizeof(action_type)); 1203 1204 if (call_data->num_params == 0 1205 || (action_type[0] == 'C' && call_data->num_params != 1) 1206 || (action_type[0] != 'C' && call_data->num_params != 2)) 1207 XtAppError(XtWidgetToApplicationContext(grip), 1208 "Paned GripAction has been passed incorrect parameters."); 1209 1210 loc = GetEventLocation(pw, (XEvent *)call_data->event); 1211 1212 if (action_type[0] != 'C') 1213 XmuNCopyISOLatin1Uppered(direction, call_data->params[1], 1214 sizeof(direction)); 1215 1216 switch (action_type[0]) { 1217 case 'S': /* Start adjustment */ 1218 pw->paned.resize_children_to_pref = False; 1219 StartGripAdjustment(pw, grip, (Direction)direction[0]); 1220 pw->paned.start_loc = loc; 1221 break; 1222 case 'M': 1223 MoveGripAdjustment(pw, grip, (Direction)direction[0], loc); 1224 break; 1225 case 'C': 1226 XtSetArg(arglist[0], XtNcursor, &cursor); 1227 XtGetValues(grip, arglist, 1); 1228 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor); 1229 CommitGripAdjustment(pw); 1230 break; 1231 default: 1232 XtAppError(XtWidgetToApplicationContext(grip), 1233 "Paned GripAction(); 1st parameter invalid"); 1234 break; 1235 } 1236} 1237 1238/* 1239 * Function: 1240 * ResortChildren 1241 * 1242 * Arguments: 1243 * pw - paned widget 1244 * 1245 * Description: 1246 * Resorts the children so that all managed children are first. 1247 */ 1248static void 1249ResortChildren(PanedWidget pw) 1250{ 1251 Widget *unmanagedP, *childP; 1252 1253 unmanagedP = NULL; 1254 ForAllChildren(pw, childP) { 1255 if (!IsPane(*childP) || !XtIsManaged(*childP)) { 1256 /* 1257 * We only keep track of the first unmanaged pane 1258 */ 1259 if (unmanagedP == NULL) 1260 unmanagedP = childP; 1261 } 1262 else { /* must be a managed pane */ 1263 /* 1264 * If an earlier widget was not a managed pane, then swap 1265 */ 1266 if (unmanagedP != NULL) { 1267 Widget child = *unmanagedP; 1268 1269 *unmanagedP = *childP; 1270 *childP = child; 1271 childP = unmanagedP; /* easiest to just back-track */ 1272 unmanagedP = NULL; /* in case there is another managed */ 1273 } 1274 } 1275 } 1276} 1277 1278/* 1279 * Function: 1280 * ManageAndUnmanageGrips 1281 * 1282 * Parameters: 1283 * pw - paned widget 1284 * 1285 * Description: 1286 * This function manages and unmanages the grips so that 1287 * the managed state of each grip matches that of its pane. 1288 */ 1289static void 1290ManageAndUnmanageGrips(PanedWidget pw) 1291{ 1292 WidgetList managed_grips, unmanaged_grips; 1293 Widget *managedP, *unmanagedP, *childP; 1294 Cardinal alloc_size; 1295 1296 alloc_size = (Cardinal)(sizeof(Widget) * (pw->composite.num_children >> 1)); 1297 managedP = managed_grips = (WidgetList)XtMalloc(alloc_size); 1298 unmanagedP = unmanaged_grips = (WidgetList)XtMalloc(alloc_size); 1299 1300 ForAllChildren(pw, childP) 1301 if (IsPane(*childP) && HasGrip(*childP)) { 1302 if (XtIsManaged(*childP)) 1303 *managedP++ = PaneInfo(*childP)->grip; 1304 else 1305 *unmanagedP++ = PaneInfo(*childP)->grip; 1306 } 1307 1308 if (managedP != managed_grips) { 1309 *unmanagedP++ = *--managedP; /* Last grip is never managed */ 1310 XtManageChildren(managed_grips, (Cardinal)(managedP - managed_grips)); 1311 } 1312 1313 if (unmanagedP != unmanaged_grips) 1314 XtUnmanageChildren(unmanaged_grips, (Cardinal)(unmanagedP - unmanaged_grips)); 1315 1316 XtFree((char *)managed_grips); 1317 XtFree((char *)unmanaged_grips); 1318} 1319 1320/* 1321 * Function: 1322 * CreateGrip 1323 * 1324 * Parameters: 1325 * child - child that wants a grip to be created for it 1326 * 1327 * Description: 1328 * Creates a grip widget. 1329 */ 1330static void 1331CreateGrip(Widget child) 1332{ 1333 PanedWidget pw = (PanedWidget)XtParent(child); 1334 Arg arglist[2]; 1335 Cardinal num_args = 0; 1336 Cursor cursor; 1337 1338 XtSetArg(arglist[num_args], XtNtranslations, pw->paned.grip_translations); 1339 num_args++; 1340 if ((cursor = pw->paned.grip_cursor) == None) { 1341 if (IsVert(pw)) 1342 cursor = pw->paned.v_grip_cursor; 1343 else 1344 cursor = pw->paned.h_grip_cursor; 1345 } 1346 1347 XtSetArg(arglist[num_args], XtNcursor, cursor); 1348 num_args++; 1349 PaneInfo(child)->grip = XtCreateWidget("grip", gripWidgetClass, (Widget)pw, 1350 arglist, num_args); 1351 1352 XtAddCallback(PaneInfo(child)->grip, XtNcallback, 1353 HandleGrip, (XtPointer)child); 1354} 1355 1356/* 1357 * Function: 1358 * GetGCs 1359 * 1360 * Parameters: 1361 * w - paned widget 1362 */ 1363static void 1364GetGCs(Widget w) 1365{ 1366 PanedWidget pw = (PanedWidget)w; 1367 XtGCMask valuemask; 1368 XGCValues values; 1369 1370 /* 1371 * Draw pane borders in internal border color 1372 */ 1373 values.foreground = pw->paned.internal_bp; 1374 valuemask = GCForeground; 1375 pw->paned.normgc = XtGetGC(w, valuemask, &values); 1376 1377 /* 1378 * Erase pane borders with background color 1379 */ 1380 values.foreground = pw->core.background_pixel; 1381 valuemask = GCForeground; 1382 pw->paned.invgc = XtGetGC(w, valuemask, &values); 1383 1384 /* 1385 * Draw Track lines (animate pane borders) in 1386 * internal border color ^ bg color 1387 */ 1388 values.function = GXinvert; 1389 values.plane_mask = pw->paned.internal_bp ^ pw->core.background_pixel; 1390 values.subwindow_mode = IncludeInferiors; 1391 valuemask = GCPlaneMask | GCFunction | GCSubwindowMode; 1392 pw->paned.flipgc = XtGetGC(w, valuemask, &values); 1393} 1394 1395/* 1396 * Function: 1397 * SetChildrenPrefSizes 1398 * 1399 * Parameters: 1400 * pw - paned widget 1401 * 1402 * Description: 1403 * Sets the preferred sizes of the children. 1404 */ 1405static void 1406SetChildrenPrefSizes(PanedWidget pw, unsigned int off_size) 1407{ 1408 Widget *childP; 1409 Boolean vert = IsVert(pw); 1410 XtWidgetGeometry request, reply; 1411 1412 ForAllPanes(pw, childP) 1413 if (pw->paned.resize_children_to_pref || PaneInfo(*childP)->size == 0 || 1414 PaneInfo(*childP)->resize_to_pref) { 1415 if (PaneInfo(*childP)->preferred_size != PANED_ASK_CHILD) 1416 PaneInfo(*childP)->wp_size = PaneInfo(*childP)->preferred_size; 1417 else { 1418 if(vert) { 1419 request.request_mode = CWWidth; 1420 request.width = (Dimension) off_size; 1421 } 1422 else { 1423 request.request_mode = CWHeight; 1424 request.height = (Dimension) off_size; 1425 } 1426 1427 if ((XtQueryGeometry(*childP, &request, &reply) 1428 == XtGeometryAlmost) 1429 && (reply.request_mode = (vert ? CWHeight : CWWidth))) 1430 PaneInfo(*childP)->wp_size = GetRequestInfo(&reply, vert); 1431 else 1432 PaneInfo(*childP)->wp_size = PaneSize(*childP, vert); 1433 } 1434 1435 PaneInfo(*childP)->size = PaneInfo(*childP)->wp_size; 1436 } 1437} 1438 1439/* 1440 * Function: 1441 * ChangeAllGripCursors 1442 * 1443 * Parameters: 1444 * pw - paned widget 1445 * 1446 * Description: 1447 * Changes all the grip cursors. 1448 */ 1449static void 1450ChangeAllGripCursors(PanedWidget pw) 1451{ 1452 Widget *childP; 1453 1454 ForAllPanes(pw, childP) { 1455 Arg arglist[1]; 1456 Cursor cursor; 1457 1458 if ((cursor = pw->paned.grip_cursor) == None) { 1459 if (IsVert(pw)) 1460 cursor = pw->paned.v_grip_cursor; 1461 else 1462 cursor = pw->paned.h_grip_cursor; 1463 } 1464 1465 if (HasGrip(*childP)) { 1466 XtSetArg(arglist[0], XtNcursor, cursor); 1467 XtSetValues(PaneInfo(*childP)->grip, arglist, 1); 1468 } 1469 } 1470} 1471 1472/* 1473 * Function: 1474 * PushPaneStack 1475 * 1476 * Parameters: 1477 * pw - paned widget 1478 * pane - pane that we are pushing 1479 * 1480 * Description: 1481 * Pushes a value onto the pane stack. 1482 */ 1483static void 1484PushPaneStack(PanedWidget pw, Pane pane) 1485{ 1486 PaneStack *stack = (PaneStack *)XtMalloc(sizeof(PaneStack)); 1487 1488 stack->next = pw->paned.stack; 1489 stack->pane = pane; 1490 stack->start_size = pane->size; 1491 1492 pw->paned.stack = stack; 1493} 1494 1495/* 1496 * Function: 1497 * GetPaneStack 1498 * 1499 * Parameters: 1500 * pw - paned widget 1501 * shrink - True if we want to shrink this pane, False otherwise 1502 * pane - pane that we are popping (return) 1503 * start_size - size that this pane started at (return) 1504 * 1505 * Description: 1506 * Gets the top value from the pane stack. 1507 */ 1508static void 1509GetPaneStack(PanedWidget pw, Bool shrink, Pane *pane, int *start_size) 1510{ 1511 if (pw->paned.stack == NULL) { 1512 *pane = NULL; 1513 return; 1514 } 1515 1516 *pane = pw->paned.stack->pane; 1517 *start_size = pw->paned.stack->start_size; 1518 1519 if (shrink != ((*pane)->size > *start_size)) 1520 *pane = NULL; 1521} 1522 1523/* 1524 * Function: 1525 * PopPaneStack 1526 * 1527 * Parameters: 1528 * pw - paned widget 1529 * 1530 * Description: 1531 * Pops the top item off the pane stack. 1532 * 1533 * Returns: True if this is not the last element on the stack 1534 */ 1535static Bool 1536PopPaneStack(PanedWidget pw) 1537{ 1538 PaneStack *stack = pw->paned.stack; 1539 1540 if (stack == NULL) 1541 return (False); 1542 1543 pw->paned.stack = stack->next; 1544 XtFree((char *)stack); 1545 1546 if (pw->paned.stack == NULL) 1547 return (False); 1548 1549 return (True); 1550} 1551 1552/* 1553 * Function: 1554 * ClearPaneStack 1555 * 1556 * Parameters: 1557 * pw - paned widget 1558 * 1559 * Description: 1560 * Removes all entries from the pane stack. 1561 */ 1562static void 1563ClearPaneStack(PanedWidget pw) 1564{ 1565 while(PopPaneStack(pw)) 1566 ; 1567} 1568 1569static void 1570XawPanedClassInitialize(void) 1571{ 1572 XawInitializeWidgetSet(); 1573 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation, 1574 NULL, 0); 1575 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString, 1576 NULL, 0, XtCacheNone, NULL); 1577} 1578 1579/* The Geometry Manager only allows changes after Realize if 1580 * allow_resize is True in the constraints record. 1581 * 1582 * For vertically paned widgets: 1583 * 1584 * It only allows height changes, but offers the requested height 1585 * as a compromise if both width and height changes were requested. 1586 * 1587 * For horizontal widgets the converse is true. 1588 * As all good Geometry Managers should, we will return No if the 1589 * request will have no effect; i.e. when the requestor is already 1590 * of the desired geometry. 1591 */ 1592static XtGeometryResult 1593XawPanedGeometryManager(Widget w, XtWidgetGeometry *request, 1594 XtWidgetGeometry *reply) 1595{ 1596 PanedWidget pw = (PanedWidget)XtParent(w); 1597 XtGeometryMask mask = request->request_mode; 1598 Dimension old_size, old_wpsize, old_paned_size; 1599 Pane pane = PaneInfo(w); 1600 Boolean vert = IsVert(pw); 1601 Dimension on_size, off_size; 1602 XtGeometryResult result; 1603 Boolean almost = False; 1604 1605 /* 1606 * If any of the following is true, disallow the geometry change 1607 * 1608 * o The paned widget is realized and allow_resize is false for the pane 1609 * o The child did not ask to change the on_size 1610 * o The request is not a width or height request 1611 * o The requested size is the same as the current size 1612 */ 1613 1614 if ((XtIsRealized((Widget)pw) && !pane->allow_resize) 1615 || !(mask & (vert ? CWHeight : CWWidth)) 1616 || (mask & (XtGeometryMask)(~(CWWidth | CWHeight))) 1617 || GetRequestInfo(request, vert) == PaneSize(w, vert)) 1618 return (XtGeometryNo); 1619 1620 old_paned_size = PaneSize((Widget)pw, vert); 1621 old_wpsize = pane->wp_size; 1622 old_size = (Dimension)pane->size; 1623 1624 pane->wp_size = (Dimension)(pane->size = GetRequestInfo(request, vert)); 1625 1626 AdjustPanedSize(pw, PaneSize((Widget)pw, !vert), &result, &on_size, 1627 &off_size); 1628 1629 /* 1630 * Fool the Refigure Locations proc to thinking that we are 1631 * a different on_size 1632 */ 1633 1634 if (result != XtGeometryNo) { 1635 if (vert) 1636 XtHeight(pw) = on_size; 1637 else 1638 XtWidth(pw) = on_size; 1639 } 1640 1641 RefigureLocations(pw, PaneIndex(w), AnyPane); 1642 1643 /* 1644 * Set up reply struct and reset core on_size 1645 */ 1646 if (vert) { 1647 XtHeight(pw) = old_paned_size; 1648 reply->height = (Dimension) pane->size; 1649 reply->width = off_size; 1650 } 1651 else { 1652 XtWidth(pw) = old_paned_size; 1653 reply->height = off_size; 1654 reply->width = (Dimension) pane->size; 1655 } 1656 1657 /* 1658 * IF either of the following is true 1659 * 1660 * o There was a "off_size" request and the new "off_size" is different 1661 * from that requested 1662 * o There was no "off_size" request and the new "off_size" is different 1663 * 1664 * o The "on_size" we will allow is different from that requested 1665 * 1666 * THEN: set almost 1667 */ 1668 if (!((vert ? CWWidth : CWHeight) & mask)) { 1669 if (vert) 1670 request->width = XtWidth(w); 1671 else 1672 request->height = XtHeight(w); 1673 } 1674 1675 almost = GetRequestInfo(request, !vert) != GetRequestInfo(reply, !vert); 1676 almost = (Boolean)(almost | (GetRequestInfo(request, vert) != GetRequestInfo(reply, vert))); 1677 1678 if ((mask & XtCWQueryOnly) || almost) { 1679 pane->wp_size = old_wpsize; 1680 pane->size = old_size; 1681 RefigureLocations(pw, PaneIndex(w), AnyPane); 1682 reply->request_mode = CWWidth | CWHeight; 1683 if (almost) 1684 return (XtGeometryAlmost); 1685 } 1686 else { 1687 AdjustPanedSize(pw, PaneSize((Widget) pw, !vert), NULL, NULL, NULL); 1688 CommitNewLocations(pw); /* layout already refigured */ 1689 } 1690 1691 return (XtGeometryDone); 1692} 1693 1694/*ARGSUSED*/ 1695static void 1696XawPanedInitialize(Widget request _X_UNUSED, Widget cnew, 1697 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1698{ 1699 PanedWidget pw = (PanedWidget)cnew; 1700 1701 GetGCs((Widget)pw); 1702 1703 pw->paned.recursively_called = False; 1704 pw->paned.stack = NULL; 1705 pw->paned.resize_children_to_pref = True; 1706 pw->paned.num_panes = 0; 1707} 1708 1709static void 1710XawPanedRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes) 1711{ 1712 PanedWidget pw = (PanedWidget)w; 1713 Widget *childP; 1714 1715 if ((attributes->cursor = pw->paned.cursor) != None) 1716 *valueMask |= CWCursor; 1717 1718 (*SuperClass->core_class.realize)(w, valueMask, attributes); 1719 1720 /* 1721 * Before we commit the new locations we need to realize all the panes and 1722 * their grips 1723 */ 1724 ForAllPanes(pw, childP) { 1725 XtRealizeWidget(*childP); 1726 if (HasGrip(*childP)) 1727 XtRealizeWidget(PaneInfo(*childP)->grip); 1728 } 1729 1730 RefigureLocationsAndCommit(w); 1731 pw->paned.resize_children_to_pref = False; 1732} 1733 1734static void 1735XawPanedDestroy(Widget w) 1736{ 1737 ReleaseGCs(w); 1738} 1739 1740static void 1741ReleaseGCs(Widget w) 1742{ 1743 PanedWidget pw = (PanedWidget)w; 1744 1745 XtReleaseGC(w, pw->paned.normgc); 1746 XtReleaseGC(w, pw->paned.invgc); 1747 XtReleaseGC(w, pw->paned.flipgc); 1748} 1749 1750static void 1751XawPanedInsertChild(Widget w) 1752{ 1753 Pane pane = PaneInfo(w); 1754 1755 /* insert the child widget in the composite children list with the 1756 superclass insert_child routine 1757 */ 1758 (*SuperClass->composite_class.insert_child)(w); 1759 1760 if (!IsPane(w)) 1761 return; 1762 1763 if (pane->show_grip == True) { 1764 CreateGrip(w); 1765 if (pane->min == PANED_GRIP_SIZE) 1766 pane->min = PaneSize(pane->grip, IsVert((PanedWidget)XtParent(w))); 1767 } 1768 else { 1769 if (pane->min == PANED_GRIP_SIZE) 1770 pane->min = 1; 1771 pane->grip = NULL; 1772 } 1773 1774 pane->size = 0; 1775 pane->paned_adjusted_me = False; 1776} 1777 1778static void 1779XawPanedDeleteChild(Widget w) 1780{ 1781 /* remove the subwidget info and destroy the grip */ 1782 if (IsPane(w) && HasGrip(w)) 1783 XtDestroyWidget(PaneInfo(w)->grip); 1784 1785 /* delete the child widget in the composite children list with the 1786 superclass delete_child routine 1787 */ 1788 (*SuperClass->composite_class.delete_child)(w); 1789} 1790 1791static void 1792XawPanedChangeManaged(Widget w) 1793{ 1794 PanedWidget pw = (PanedWidget)w; 1795 Boolean vert = IsVert(pw); 1796 Dimension size; 1797 Widget *childP; 1798 1799 if (pw->paned.recursively_called++) 1800 return; 1801 1802 /* 1803 * If the size is zero then set it to the size of the widest or tallest pane 1804 */ 1805 1806 if ((size = PaneSize((Widget)pw, !vert)) == 0) { 1807 size = 1; 1808 ForAllChildren(pw, childP) 1809 if (XtIsManaged(*childP) && (PaneSize(*childP, !vert) > size)) 1810 size = PaneSize(*childP, !vert); 1811 } 1812 1813 ManageAndUnmanageGrips(pw); 1814 pw->paned.recursively_called = False; 1815 ResortChildren(pw); 1816 1817 pw->paned.num_panes = 0; 1818 ForAllChildren(pw, childP) 1819 if (IsPane(*childP)) { 1820 if (XtIsManaged(*childP)) { 1821 Pane pane = PaneInfo(*childP); 1822 1823 if (HasGrip(*childP)) 1824 PaneInfo(pane->grip)->position = pw->paned.num_panes; 1825 pane->position = pw->paned.num_panes; /* TEMPORARY -CDP 3/89 */ 1826 pw->paned.num_panes++; 1827 } 1828 else 1829 break; /* This list is already sorted */ 1830 } 1831 1832 SetChildrenPrefSizes((PanedWidget) w, size); 1833 1834 /* 1835 * ForAllPanes can now be used 1836 */ 1837 if (PaneSize((Widget) pw, vert) == 0) 1838 AdjustPanedSize(pw, size, NULL, NULL, NULL); 1839 1840 if (XtIsRealized((Widget)pw)) 1841 RefigureLocationsAndCommit((Widget)pw); 1842} 1843 1844static void 1845XawPanedResize(Widget w) 1846{ 1847 SetChildrenPrefSizes((PanedWidget)w, 1848 PaneSize(w, !IsVert((PanedWidget)w))); 1849 RefigureLocationsAndCommit(w); 1850} 1851 1852/*ARGSUSED*/ 1853static void 1854XawPanedRedisplay(Widget w, XEvent *event _X_UNUSED, Region region _X_UNUSED) 1855{ 1856 DrawInternalBorders((PanedWidget)w); 1857} 1858 1859/*ARGSUSED*/ 1860static Boolean 1861XawPanedSetValues(Widget old, Widget request _X_UNUSED, Widget cnew, 1862 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1863{ 1864 PanedWidget old_pw = (PanedWidget)old; 1865 PanedWidget new_pw = (PanedWidget)cnew; 1866 Boolean redisplay = False; 1867 1868 if ((old_pw->paned.cursor != new_pw->paned.cursor) && XtIsRealized(cnew)) 1869 XDefineCursor(XtDisplay(cnew), XtWindow(cnew), new_pw->paned.cursor); 1870 1871 if (old_pw->paned.internal_bp != new_pw->paned.internal_bp || 1872 old_pw->core.background_pixel != new_pw->core.background_pixel) { 1873 ReleaseGCs(old); 1874 GetGCs(cnew); 1875 redisplay = True; 1876 } 1877 1878 if (old_pw->paned.grip_cursor != new_pw->paned.grip_cursor || 1879 old_pw->paned.v_grip_cursor != new_pw->paned.v_grip_cursor || 1880 old_pw->paned.h_grip_cursor != new_pw->paned.h_grip_cursor) 1881 ChangeAllGripCursors(new_pw); 1882 1883 if (IsVert(old_pw) != IsVert(new_pw)) { 1884 /* 1885 * We are fooling the paned widget into thinking that is needs to 1886 * fully refigure everything, which is what we want 1887 */ 1888 if (IsVert(new_pw)) 1889 XtWidth(new_pw) = 0; 1890 else 1891 XtHeight(new_pw) = 0; 1892 1893 new_pw->paned.resize_children_to_pref = True; 1894 XawPanedChangeManaged(cnew); /* Seems weird, but does the right thing */ 1895 new_pw->paned.resize_children_to_pref = False; 1896 if (new_pw->paned.grip_cursor == None) 1897 ChangeAllGripCursors(new_pw); 1898 return (True); 1899 } 1900 1901 if (old_pw->paned.internal_bw != new_pw->paned.internal_bw) { 1902 AdjustPanedSize(new_pw, PaneSize(cnew, !IsVert(old_pw)), 1903 NULL, NULL, NULL); 1904 RefigureLocationsAndCommit(cnew); 1905 return (True); /* We have done a full configuration, return */ 1906 } 1907 1908 if (old_pw->paned.grip_indent != new_pw->paned.grip_indent && 1909 XtIsRealized(cnew)) { 1910 CommitNewLocations(new_pw); 1911 redisplay = True; 1912 } 1913 1914 return (redisplay); 1915} 1916 1917/*ARGSUSED*/ 1918static Boolean 1919XawPanedPaneSetValues(Widget old, Widget request _X_UNUSED, Widget cnew, 1920 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED) 1921{ 1922 Pane old_pane = PaneInfo(old); 1923 Pane new_pane = PaneInfo(cnew); 1924 Boolean redisplay = False; 1925 1926 /* Check for new min and max */ 1927 if (old_pane->min != new_pane->min || old_pane->max != new_pane->max) 1928 XawPanedSetMinMax(cnew, (int)new_pane->min, (int)new_pane->max); 1929 1930 /* Check for change in XtNshowGrip */ 1931 if (old_pane->show_grip != new_pane->show_grip) { 1932 if (new_pane->show_grip == True) { 1933 CreateGrip(cnew); 1934 if (XtIsRealized(XtParent(cnew))) { 1935 if (XtIsManaged(cnew)) /* if paned is unrealized this will 1936 happen automatically at realize time 1937 */ 1938 XtManageChild(PaneInfo(cnew)->grip); /* manage the grip */ 1939 XtRealizeWidget(PaneInfo(cnew)->grip); /* realize the grip */ 1940 CommitNewLocations((PanedWidget)XtParent(cnew)); 1941 } 1942 } 1943 else if (HasGrip(old)) { 1944 XtDestroyWidget(old_pane->grip); 1945 new_pane->grip = NULL; 1946 redisplay = True; 1947 } 1948 } 1949 1950 return (redisplay); 1951} 1952 1953/* 1954 * Public routines 1955 */ 1956/* 1957 * Function: 1958 * XawPanedSetMinMax 1959 * 1960 * Parameters: 1961 * widget - widget that is a child of the Paned widget 1962 * min - new min and max size for the pane 1963 * max - "" 1964 * 1965 * Description: 1966 * Sets the min and max size for a pane. 1967 */ 1968void 1969XawPanedSetMinMax(Widget widget, int min, int max) 1970{ 1971 Pane pane = PaneInfo(widget); 1972 1973 pane->min = (Dimension) min; 1974 pane->max = (Dimension) max; 1975 RefigureLocationsAndCommit(widget->core.parent); 1976} 1977 1978/* 1979 * Function: 1980 * XawPanedGetMinMax 1981 * 1982 * Parameters: 1983 * widget - widget that is a child of the Paned widget 1984 * min - current min and max size for the pane (return) 1985 * max - "" 1986 * 1987 * Description: 1988 * Gets the min and max size for a pane. 1989 */ 1990void 1991XawPanedGetMinMax(Widget widget, int *min, int *max) 1992{ 1993 Pane pane = PaneInfo(widget); 1994 1995 *min = pane->min; 1996 *max = pane->max; 1997} 1998 1999/* 2000 * Function: 2001 * XawPanedSetRefigureMode 2002 * 2003 * Parameters: 2004 * w - paned widget 2005 * mode - if False then inhibit refigure 2006 * 2007 * Description: 2008 * Allows a flag to be set the will inhibit 2009 * the paned widgets relayout routine. 2010 */ 2011void 2012XawPanedSetRefigureMode(Widget w, 2013#if NeedWidePrototypes 2014 int mode 2015#else 2016 Boolean mode 2017#endif 2018) 2019{ 2020 ((PanedWidget)w)->paned.refiguremode = mode; 2021 RefigureLocationsAndCommit(w); 2022} 2023 2024/* 2025 * Function: 2026 * XawPanedGetNumSub 2027 * 2028 * Parameters: 2029 * w - paned widget 2030 * 2031 * Description: 2032 * Returns the number of panes in the paned widget. 2033 * Returns: 2034 * the number of panes in the paned widget 2035 */ 2036int 2037XawPanedGetNumSub(Widget w) 2038{ 2039 return (((PanedWidget)w)->paned.num_panes); 2040} 2041 2042/* 2043 * Function: 2044 * XawPanedAllowResize 2045 * 2046 * Parameters: 2047 * widget - child of the paned widget 2048 * 2049 * Description: 2050 * Allows a flag to be set that determines if the paned 2051 * widget will allow geometry requests from this child. 2052 */ 2053void 2054XawPanedAllowResize(Widget widget, 2055#if NeedWidePrototypes 2056 int allow_resize 2057#else 2058 Boolean allow_resize 2059#endif 2060) 2061{ 2062 PaneInfo(widget)->allow_resize = allow_resize; 2063} 2064