1 /***********************************************************
3 Copyright 1987, 1988, 1994, 1998 The Open Group
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
26 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
30 Permission to use, copy, modify, and distribute this software and its
31 documentation for any purpose and without fee is hereby granted,
32 provided that the above copyright notice appear in all copies and that
33 both that copyright notice and this permission notice appear in
34 supporting documentation, and that the name of Digital not be
35 used in advertising or publicity pertaining to distribution of the
36 software without specific, written prior permission.
38 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 ******************************************************************/
49 * Updated and significantly modified from the Athena VPaned Widget.
53 * By: Chris D. Peterson
55 * kit@expo.lcs.mit.edu
61 #include <X11/IntrinsicP.h>
62 #include <X11/cursorfont.h>
63 #include <X11/StringDefs.h>
64 #include <X11/Xmu/CharSet.h>
65 #include <X11/Xmu/Converters.h>
66 #include <X11/Xmu/Misc.h>
67 #include <X11/Xmu/SysUtil.h>
68 #include <X11/Xaw/Grip.h>
69 #include <X11/Xaw/PanedP.h>
70 #include <X11/Xaw/XawImP.h>
71 #include <X11/Xaw/XawInit.h>
84 #define PaneInfo(w) ((Pane)(w)->core.constraints)
85 #define HasGrip(w) (PaneInfo(w)->grip != NULL)
86 #define IsPane(w) ((w)->core.widget_class != gripWidgetClass)
87 #define PaneIndex(w) (PaneInfo(w)->position)
88 #define IsVert(w) ((w)->paned.orientation == XtorientVertical)
90 #define ForAllPanes(pw, childP) \
91 for ((childP) = (pw)->composite.children; \
92 (childP) < (pw)->composite.children + (pw)->paned.num_panes; \
95 #define ForAllChildren(pw, childP) \
96 for ((childP) = (pw)->composite.children; \
97 (childP) < (pw)->composite.children + (pw)->composite.num_children; \
100 #define PaneSize(paned, vertical) \
101 ((vertical) ? XtHeight(paned) : XtWidth(paned))
103 #define GetRequestInfo(geo, vertical) \
104 ((vertical) ? (geo)->height : (geo)->width)
106 #define SatisfiesRule1(pane, shrink) \
107 (((shrink) && ((pane)->size != (pane)->min)) \
108 || (!(shrink) && ((pane)->size != (pane)->max)))
110 #define SatisfiesRule2(pane) \
111 (!(pane)->skip_adjust || (pane)->paned_adjusted_me)
113 #define SatisfiesRule3(pane, shrink) \
114 ((pane)->paned_adjusted_me \
115 && (((shrink) && ((int)(pane)->wp_size <= (pane)->size)) \
116 || (!(shrink) && ((int)(pane)->wp_size >= (pane)->size))))
122 static void XawPanedClassInitialize(void);
123 static void XawPanedChangeManaged(Widget);
124 static void XawPanedDeleteChild(Widget);
125 static void XawPanedDestroy(Widget);
126 static XtGeometryResult XawPanedGeometryManager(Widget, XtWidgetGeometry*,
128 static void XawPanedInitialize(Widget, Widget, ArgList, Cardinal*);
129 static void XawPanedInsertChild(Widget);
130 static Boolean XawPanedPaneSetValues(Widget, Widget, Widget,
132 static void XawPanedRealize(Widget, Mask*, XSetWindowAttributes*);
133 static void XawPanedRedisplay(Widget, XEvent*, Region);
134 static void XawPanedResize(Widget);
135 static Boolean XawPanedSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
140 static void _DrawInternalBorders(PanedWidget, GC);
141 static void _DrawRect(PanedWidget, GC, int, int, unsigned int, unsigned int);
142 static void _DrawTrackLines(PanedWidget, Bool);
143 static void AdjustPanedSize(PanedWidget, unsigned int, XtGeometryResult*,
144 Dimension*, Dimension*);
145 static void ChangeAllGripCursors(PanedWidget);
146 static Pane ChoosePaneToResize(PanedWidget, int, Direction, Bool);
147 static void ClearPaneStack(PanedWidget);
148 static void CommitGripAdjustment(PanedWidget);
149 static void CreateGrip(Widget);
150 static int GetEventLocation(PanedWidget, XEvent*);
151 static void GetGCs(Widget);
152 static void GetPaneStack(PanedWidget, Bool, Pane*, int*);
153 static void HandleGrip(Widget, XtPointer, XtPointer);
154 static void LoopAndRefigureChildren(PanedWidget, int, Direction, int*);
155 static void ManageAndUnmanageGrips(PanedWidget);
156 static void MoveGripAdjustment(PanedWidget, Widget, Direction, int);
157 static Bool PopPaneStack(PanedWidget);
158 static void PushPaneStack(PanedWidget, Pane);
159 static void RefigureLocations(PanedWidget, int, Direction);
160 static void RefigureLocationsAndCommit(Widget);
161 static void ReleaseGCs(Widget);
162 static void ResortChildren(PanedWidget);
163 static void SetChildrenPrefSizes(PanedWidget, unsigned int);
164 static void StartGripAdjustment(PanedWidget, Widget, Direction);
169 static char defGripTranslations[] =
170 "<Btn1Down>:" "GripAction(Start,UpLeftPane)\n"
171 "<Btn2Down>:" "GripAction(Start,ThisBorderOnly)\n"
172 "<Btn3Down>:" "GripAction(Start,LowRightPane)\n"
173 "<Btn1Motion>:" "GripAction(Move,UpLeft)\n"
174 "<Btn2Motion>:" "GripAction(Move,ThisBorder)\n"
175 "<Btn3Motion>:" "GripAction(Move,LowRight)\n"
176 "Any<BtnUp>:" "GripAction(Commit)\n"
179 #define offset(field) XtOffsetOf(PanedRec, paned.field)
180 static XtResource resources[] = {
182 XtNinternalBorderColor,
188 (XtPointer)XtDefaultForeground
191 XtNinternalBorderWidth,
213 offset(refiguremode),
221 sizeof(XtTranslations),
222 offset(grip_translations),
224 (XtPointer)defGripTranslations
230 sizeof(XtOrientation),
233 (XtPointer)XtorientVertical
254 XtNverticalGripCursor,
258 offset(v_grip_cursor),
263 XtNhorizontalGripCursor,
267 offset(h_grip_cursor),
276 offset(adjust_this_cursor),
281 XtNverticalBetweenCursor,
285 offset(v_adjust_this_cursor),
290 XtNhorizontalBetweenCursor,
294 offset(h_adjust_this_cursor),
303 offset(adjust_upper_cursor),
312 offset(adjust_lower_cursor),
321 offset(adjust_left_cursor),
330 offset(adjust_right_cursor),
337 #define offset(field) XtOffsetOf(PanedConstraintsRec, paned.field)
338 static XtResource subresources[] = {
344 offset(allow_resize),
364 (XtPointer)PANED_GRIP_SIZE
376 XtNpreferredPaneSize,
377 XtCPreferredPaneSize,
380 offset(preferred_size),
382 (XtPointer)PANED_ASK_CHILD
385 XtNresizeToPreferred,
389 offset(resize_to_pref),
414 #define SuperClass ((ConstraintWidgetClass)&constraintClassRec)
416 PanedClassRec panedClassRec = {
419 (WidgetClass)SuperClass, /* superclass */
420 "Paned", /* class name */
421 sizeof(PanedRec), /* size */
422 XawPanedClassInitialize, /* class_initialize */
423 NULL, /* class_part init */
424 False, /* class_inited */
425 XawPanedInitialize, /* initialize */
426 NULL, /* initialize_hook */
427 XawPanedRealize, /* realize */
430 resources, /* resources */
431 XtNumber(resources), /* num_resources */
432 NULLQUARK, /* xrm_class */
433 True, /* compress_motion */
434 True, /* compress_exposure */
435 True, /* compress_enterleave */
436 False, /* visible_interest */
437 XawPanedDestroy, /* destroy */
438 XawPanedResize, /* resize */
439 XawPanedRedisplay, /* expose */
440 XawPanedSetValues, /* set_values */
441 NULL, /* set_values_hook */
442 XtInheritSetValuesAlmost, /* set_values_almost */
443 NULL, /* get_values_hook */
444 NULL, /* accept_focus */
445 XtVersion, /* version */
446 NULL, /* callback_private */
448 XtInheritQueryGeometry, /* query_geometry */
449 XtInheritDisplayAccelerator, /* display_accelerator */
450 NULL, /* extension */
454 XawPanedGeometryManager, /* geometry_manager */
455 XawPanedChangeManaged, /* change_managed */
456 XawPanedInsertChild, /* insert_child */
457 XawPanedDeleteChild, /* delete_child */
458 NULL, /* extension */
462 subresources, /* subresources */
463 XtNumber(subresources), /* subresource_count */
464 sizeof(PanedConstraintsRec), /* constraint_size */
465 NULL, /* initialize */
467 XawPanedPaneSetValues, /* set_values */
468 NULL, /* extension */
472 WidgetClass panedWidgetClass = (WidgetClass)&panedClassRec;
473 WidgetClass vPanedWidgetClass = (WidgetClass)&panedClassRec;
482 * pw - paned widget to adjust
483 * off_size - new off_size to use
484 * result_ret - result of query (return)
485 * on_size_ret - new on_size (return)
486 * off_size_ret - new off_size (return)
489 * Adjusts the size of the pane.
492 * amount of change in size
495 AdjustPanedSize(PanedWidget pw, unsigned int off_size,
496 XtGeometryResult *result_ret,
497 Dimension *on_size_ret, Dimension *off_size_ret)
499 Dimension old_size = PaneSize((Widget)pw, IsVert(pw));
500 Dimension newsize = 0;
502 XtWidgetGeometry request, reply;
504 request.request_mode = CWWidth | CWHeight;
506 ForAllPanes(pw, childP) {
507 int size = Max(PaneInfo(*childP)->size, (int)PaneInfo(*childP)->min);
509 AssignMin(size, (int)PaneInfo(*childP)->max);
510 newsize += size + pw->paned.internal_bw;
512 newsize -= pw->paned.internal_bw;
518 request.width = off_size;
519 request.height = newsize;
522 request.width = newsize;
523 request.height = off_size;
526 if (result_ret != NULL) {
527 request.request_mode |= XtCWQueryOnly;
529 *result_ret = XtMakeGeometryRequest((Widget)pw, &request, &reply);
530 _XawImCallVendorShellExtResize((Widget)pw);
532 if (newsize == old_size || *result_ret == XtGeometryNo) {
533 *on_size_ret = old_size;
534 *off_size_ret = off_size;
537 if (*result_ret != XtGeometryAlmost) {
538 *on_size_ret = GetRequestInfo(&request, IsVert(pw));
539 *off_size_ret = GetRequestInfo(&request, !IsVert(pw));
542 *on_size_ret = GetRequestInfo(&reply, IsVert(pw));
543 *off_size_ret = GetRequestInfo(&reply, !IsVert(pw));
547 if (newsize == old_size)
550 if (XtMakeGeometryRequest((Widget)pw, &request, &reply) == XtGeometryAlmost)
551 XtMakeGeometryRequest((Widget)pw, &reply, &request);
556 * ChoosePaneToResize.
560 * paneindex - index of the current pane
561 * dir - direction to search first
562 * shrink - True if we need to shrink a pane, False otherwise
565 * This function chooses a pane to resize.
566 They are chosen using the following rules:
568 * 1) size < max && size > min
569 * 2) skip adjust == False
570 * 3) widget not its prefered height
571 * && this change will bring it closer
572 * && The user has not resized this pane.
574 * If no widgets are found that fits all the rules then
576 * If there are still no widgets found than
578 * Rule #1 is never broken.
579 * If no widgets are found then NULL is returned.
582 * pane to resize or NULL
585 ChoosePaneToResize(PanedWidget pw, int paneindex, Direction dir, Bool shrink)
589 Direction _dir = dir;
590 int _index = paneindex;
592 if (paneindex == NO_INDEX || dir == AnyPane) { /* Use defaults */
593 _dir = LowRightPane; /* Go up - really */
594 _index = pw->paned.num_panes - 1; /* Start the last pane, and work
597 childP = pw->composite.children + _index;
601 Pane pane = PaneInfo(*childP);
603 if ((rules < 3 || SatisfiesRule3(pane, shrink))
604 && (rules < 2 || SatisfiesRule2(pane))
605 && SatisfiesRule1(pane, shrink)
606 && (paneindex != PaneIndex(*childP) || dir == AnyPane))
610 * This is counter-intuitive, but if we are resizing the pane
611 * above the grip we want to choose a pane below the grip to lose,
614 if (_dir == LowRightPane)
620 * If we have come to and edge then reduce the rule set, and try again
621 * If we are reduced the rules to none, then return NULL
623 if ((childP - pw->composite.children) < 0 ||
624 (childP - pw->composite.children) >= pw->paned.num_panes) {
625 if (--rules < 1) /* less strict rules */
627 childP = pw->composite.children + _index;
634 * LoopAndRefigureChildren
638 * paneindex - number of the pane border we are moving
639 * dir - pane to move (either UpLeftPane or LowRightPane)
640 * sizeused - current amount of space used (used and returned)
643 * If we are resizing either the UpleftPane or LowRight Pane loop
644 * through all the children to see if any will allow us to resize them.
647 LoopAndRefigureChildren(PanedWidget pw, int paneindex, Direction dir,
650 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw));
651 Boolean shrink = (*sizeused > pane_size);
653 if (dir == LowRightPane)
656 /* While all panes do not fit properly */
657 while (*sizeused != pane_size) {
659 * Choose a pane to resize
660 * First look on the Pane Stack, and then go hunting for another one
661 * If we fail to find a pane to resize then give up
666 Boolean rule3_ok = False, from_stack = True;
668 GetPaneStack(pw, shrink, &pane, &start_size);
670 pane = ChoosePaneToResize(pw, paneindex, dir, shrink);
672 return; /* no one to resize, give up */
674 rule3_ok = SatisfiesRule3(pane, shrink);
676 PushPaneStack(pw, pane);
680 * Try to resize this pane so that all panes will fit, take min and max
684 pane->size += pane_size - *sizeused;
688 AssignMax(pane->size, start_size);
689 } /* don't remove these braces */
691 AssignMin(pane->size, start_size);
693 if (pane->size == start_size)
694 (void)PopPaneStack(pw);
698 AssignMax(pane->size, (int)pane->wp_size);
699 } /* don't remove these braces */
701 AssignMin(pane->size, (int)pane->wp_size);
704 pane->paned_adjusted_me = pane->size != pane->wp_size;
705 AssignMax(pane->size, (int)pane->min);
706 AssignMin(pane->size, (int)pane->max);
707 *sizeused += (pane->size - old);
717 * paneindex - child to start refiguring at
718 * dir - direction to move from child
721 * Refigures all locations of children.
722 * There are special arguments to paneindex and dir, they are:
723 * paneindex - NO_INDEX.
726 * If either of these is true then all panes may be resized and
727 * the choosing of panes procedes in reverse order starting with the
731 RefigureLocations(PanedWidget pw, int paneindex, Direction dir)
734 int pane_size = (int)PaneSize((Widget)pw, IsVert(pw));
738 if (pw->paned.num_panes == 0 || !pw->paned.refiguremode)
742 * Get an initial estimate of the size we will use
744 ForAllPanes(pw, childP) {
745 Pane pane = PaneInfo(*childP);
747 AssignMax(pane->size, (int) pane->min);
748 AssignMin(pane->size, (int) pane->max);
749 sizeused += (int)pane->size + (int)pw->paned.internal_bw;
751 sizeused -= (int)pw->paned.internal_bw;
753 if (dir != ThisBorderOnly && sizeused != pane_size)
754 LoopAndRefigureChildren(pw, paneindex, dir, &sizeused);
757 * If we still are not the right size, then tell the pane that
758 * wanted to resize that it can't
760 if (paneindex != NO_INDEX && dir != AnyPane) {
761 Pane pane = PaneInfo(*(pw->composite.children + paneindex));
762 Dimension old = pane->size;
764 pane->size += pane_size - sizeused;
765 AssignMax(pane->size, (int) pane->min);
766 AssignMin(pane->size, (int) pane->max);
767 sizeused += pane->size - old;
771 * It is possible that the panes will not fit inside the vpaned widget, but
772 * we have tried out best
774 * Assign each pane a location
776 ForAllPanes(pw, childP) {
777 PaneInfo(*childP)->delta = loc;
778 loc += PaneInfo(*childP)->size + pw->paned.internal_bw;
790 * Commits all of the previously figured locations.
793 CommitNewLocations(PanedWidget pw)
796 XWindowChanges changes;
798 changes.stack_mode = Above;
800 ForAllPanes(pw, childP) {
801 Pane pane = PaneInfo(*childP);
802 Widget grip = pane->grip; /* may be NULL */
805 XtMoveWidget(*childP, (Position) 0, pane->delta);
806 XtResizeWidget(*childP, XtWidth(pw), pane->size, 0);
808 if (HasGrip(*childP)) { /* Move and Display the Grip */
809 changes.x = XtWidth(pw) - pw->paned.grip_indent -
810 XtWidth(grip) - (XtBorderWidth(grip) << 1);
811 changes.y = XtY(*childP) + XtHeight(*childP) -
812 (XtHeight(grip) >> 1) - XtBorderWidth(grip) +
813 (pw->paned.internal_bw >> 1);
817 XtMoveWidget(*childP, pane->delta, 0);
818 XtResizeWidget(*childP, pane->size, XtHeight(pw), 0);
820 if (HasGrip(*childP)) { /* Move and Display the Grip */
821 changes.x = XtX(*childP) + XtWidth(*childP) -
822 (XtWidth(grip) >> 1) - XtBorderWidth(grip) +
823 (pw->paned.internal_bw >> 1);
824 changes.y = XtHeight(pw) - pw->paned.grip_indent -
825 XtHeight(grip) - (XtBorderWidth(grip) << 1);
830 * This should match XtMoveWidget, except that we're also insuring the
831 * grip is Raised in the same request
834 if (HasGrip(*childP)) {
835 XtX(grip) = changes.x;
836 XtY(grip) = changes.y;
838 if (XtIsRealized(pane->grip))
839 XConfigureWindow(XtDisplay(pane->grip), XtWindow(pane->grip),
840 CWX | CWY | CWStackMode, &changes);
848 * RefigureLocationsAndCommit
854 * Refigures all locations in a paned widget and commits them immediately.
856 * This function does nothing if any of the following are true.
857 * o refiguremode is false.
858 * o The widget is unrealized.
859 * o There are no panes is the paned widget.
862 RefigureLocationsAndCommit(Widget w)
864 PanedWidget pw = (PanedWidget)w;
866 if (pw->paned.refiguremode && XtIsRealized(w) && pw->paned.num_panes > 0) {
867 RefigureLocations(pw, NO_INDEX, AnyPane);
868 CommitNewLocations(pw);
878 * gc - gc to used for the draw
879 * on_olc - location of upper left corner of rect
881 * on_size - size of rectangle
885 * Draws a rectangle in the proper orientation.
888 _DrawRect(PanedWidget pw, GC gc, int on_loc, int off_loc,
889 unsigned int on_size, unsigned int off_size)
892 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc,
893 off_loc, on_loc, off_size, on_size);
895 XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc,
896 on_loc, off_loc, on_size, off_size);
901 * _DrawInternalBorders
905 * gc - GC to use to draw the borders
908 * Draws the internal borders into the paned widget.
911 _DrawInternalBorders(PanedWidget pw, GC gc)
915 unsigned int on_size, off_size;
918 * This is an optimization. Do not paint the internal borders if
919 * they are the same color as the background
921 if (pw->core.background_pixel == pw->paned.internal_bp)
925 off_size = (unsigned int) PaneSize((Widget)pw, !IsVert(pw));
926 on_size = (unsigned int)pw->paned.internal_bw;
928 ForAllPanes(pw, childP) {
929 on_loc = IsVert(pw) ? XtY(*childP) : XtX(*childP);
930 on_loc -= (int)on_size;
932 _DrawRect(pw, gc, on_loc, off_loc, on_size, off_size);
936 #define DrawInternalBorders(pw) \
937 _DrawInternalBorders((pw), (pw)->paned.normgc)
938 #define EraseInternalBorders(pw) \
939 _DrawInternalBorders((pw), (pw)->paned.invgc)
946 * erase - if True then just erase track lines, else draw them in
949 * Draws the lines that animate the pane borders when the grips are moved.
952 _DrawTrackLines(PanedWidget pw, Bool erase)
957 unsigned int on_size, off_size;
960 off_size = PaneSize((Widget)pw, !IsVert(pw));
962 ForAllPanes(pw, childP) {
963 pane = PaneInfo(*childP);
964 if (erase || pane->olddelta != pane->delta) {
965 on_size = pw->paned.internal_bw;
967 on_loc = PaneInfo(*childP)->olddelta - (int) on_size;
968 _DrawRect(pw, pw->paned.flipgc,
969 on_loc, off_loc, on_size, off_size);
972 on_loc = PaneInfo(*childP)->delta - (int)on_size;
974 _DrawRect(pw, pw->paned.flipgc,
975 on_loc, off_loc, on_size, off_size);
977 pane->olddelta = pane->delta;
982 #define DrawTrackLines(pw) _DrawTrackLines((pw), False);
983 #define EraseTrackLines(pw) _DrawTrackLines((pw), True);
989 * pw - the paned widget
990 * event - pointer to an event
993 * Converts and event to an x and y location.
996 * if this is a vertical pane then (y) else (x)
999 GetEventLocation(PanedWidget pw, XEvent *event)
1003 switch (event->xany.type) {
1006 x = event->xbutton.x_root;
1007 y = event->xbutton.y_root;
1011 x = event->xkey.x_root;
1012 y = event->xkey.y_root;
1015 x = event->xmotion.x_root;
1016 y = event->xmotion.y_root;
1019 x = pw->paned.start_loc;
1020 y = pw->paned.start_loc;
1031 * StartGripAdjustment
1035 * grip - grip widget selected
1036 * dir - direction that we are to be moving
1039 * Starts the grip adjustment procedure.
1042 StartGripAdjustment(PanedWidget pw, Widget grip, Direction dir)
1047 pw->paned.whichadd = pw->paned.whichsub = NULL;
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];
1057 if (XtIsRealized(grip)) {
1059 if (dir == UpLeftPane)
1060 cursor = pw->paned.adjust_upper_cursor;
1061 else if (dir == LowRightPane)
1062 cursor = pw->paned.adjust_lower_cursor;
1064 if (pw->paned.adjust_this_cursor == None)
1065 cursor = pw->paned.v_adjust_this_cursor;
1067 cursor = pw->paned.adjust_this_cursor;
1071 if (dir == UpLeftPane)
1072 cursor = pw->paned.adjust_left_cursor;
1073 else if (dir == LowRightPane)
1074 cursor = pw->paned.adjust_right_cursor;
1076 if (pw->paned.adjust_this_cursor == None)
1077 cursor = pw->paned.h_adjust_this_cursor;
1079 cursor = pw->paned.adjust_this_cursor;
1083 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor);
1086 EraseInternalBorders(pw);
1087 ForAllPanes(pw, childP)
1088 PaneInfo(*childP)->olddelta = -99;
1090 EraseTrackLines(pw);
1095 * MoveGripAdjustment
1099 * grip - grip that we are moving
1100 * dir - direction the pane we are interested is w.r.t the grip
1101 * loc - location of pointer in proper direction
1104 * This routine moves all panes around when a grip is moved.
1107 MoveGripAdjustment(PanedWidget pw, Widget grip, Direction dir, int loc)
1109 int diff, add_size = 0, sub_size = 0;
1111 diff = loc - pw->paned.start_loc;
1113 if (pw->paned.whichadd)
1114 add_size = PaneSize(pw->paned.whichadd, IsVert(pw)) + diff;
1116 if (pw->paned.whichsub)
1117 sub_size = PaneSize(pw->paned.whichsub, IsVert(pw)) - diff;
1120 * If moving this border only then do not allow either of the borders
1121 * to go beyond the min or max size allowed
1123 if (dir == ThisBorderOnly) {
1124 int old_add_size = add_size, old_sub_size;
1126 AssignMax(add_size, (int)PaneInfo(pw->paned.whichadd)->min);
1127 AssignMin(add_size, (int)PaneInfo(pw->paned.whichadd)->max);
1128 if (add_size != old_add_size)
1129 sub_size += old_add_size - add_size;
1131 old_sub_size = sub_size;
1132 AssignMax(sub_size, (int)PaneInfo(pw->paned.whichsub)->min);
1133 AssignMin(sub_size, (int)PaneInfo(pw->paned.whichsub)->max);
1134 if (sub_size != old_sub_size)
1135 return; /* Abort to current sizes */
1139 PaneInfo(pw->paned.whichadd)->size = add_size;
1141 PaneInfo(pw->paned.whichsub)->size = sub_size;
1142 RefigureLocations(pw, PaneIndex(grip), dir);
1148 * CommitGripAdjustment
1154 * Commits the grip adjustment.
1157 CommitGripAdjustment(PanedWidget pw)
1159 EraseTrackLines(pw);
1160 CommitNewLocations(pw);
1161 DrawInternalBorders(pw);
1164 * Since the user selected this size then use it as the preferred size
1166 if (pw->paned.whichadd) {
1167 Pane pane = PaneInfo(pw->paned.whichadd);
1169 pane->wp_size = pane->size;
1171 if (pw->paned.whichsub) {
1172 Pane pane = PaneInfo(pw->paned.whichsub);
1174 pane->wp_size = pane->size;
1183 * grip - grip widget that has been moved
1185 * call_data - data passed to us from the grip widget
1188 * Handles the grip manipulations.
1192 HandleGrip(Widget grip, XtPointer temp, XtPointer callData)
1194 XawGripCallData call_data = (XawGripCallData)callData;
1195 PanedWidget pw = (PanedWidget) XtParent(grip);
1197 char action_type[2], direction[2];
1201 if (call_data->num_params)
1202 XmuNCopyISOLatin1Uppered(action_type, call_data->params[0],
1203 sizeof(action_type));
1205 if (call_data->num_params == 0
1206 || (action_type[0] == 'C' && call_data->num_params != 1)
1207 || (action_type[0] != 'C' && call_data->num_params != 2))
1208 XtAppError(XtWidgetToApplicationContext(grip),
1209 "Paned GripAction has been passed incorrect parameters.");
1211 loc = GetEventLocation(pw, (XEvent *)call_data->event);
1213 if (action_type[0] != 'C')
1214 XmuNCopyISOLatin1Uppered(direction, call_data->params[1],
1217 switch (action_type[0]) {
1218 case 'S': /* Start adjustment */
1219 pw->paned.resize_children_to_pref = False;
1220 StartGripAdjustment(pw, grip, (Direction)direction[0]);
1221 pw->paned.start_loc = loc;
1224 MoveGripAdjustment(pw, grip, (Direction)direction[0], loc);
1227 XtSetArg(arglist[0], XtNcursor, &cursor);
1228 XtGetValues(grip, arglist, 1);
1229 XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor);
1230 CommitGripAdjustment(pw);
1233 XtAppError(XtWidgetToApplicationContext(grip),
1234 "Paned GripAction(); 1st parameter invalid");
1247 * Resorts the children so that all managed children are first.
1250 ResortChildren(PanedWidget pw)
1252 Widget *unmanagedP, *childP;
1255 ForAllChildren(pw, childP) {
1256 if (!IsPane(*childP) || !XtIsManaged(*childP)) {
1258 * We only keep track of the first unmanaged pane
1260 if (unmanagedP == NULL)
1261 unmanagedP = childP;
1263 else { /* must be a managed pane */
1265 * If an earlier widget was not a managed pane, then swap
1267 if (unmanagedP != NULL) {
1268 Widget child = *unmanagedP;
1270 *unmanagedP = *childP;
1272 childP = unmanagedP; /* easiest to just back-track */
1273 unmanagedP = NULL; /* in case there is another managed */
1281 * ManageAndUnmanageGrips
1287 * This function manages and unmanages the grips so that
1288 * the managed state of each grip matches that of its pane.
1291 ManageAndUnmanageGrips(PanedWidget pw)
1293 WidgetList managed_grips, unmanaged_grips;
1294 Widget *managedP, *unmanagedP, *childP;
1295 Cardinal alloc_size;
1297 alloc_size = sizeof(Widget) * (pw->composite.num_children >> 1);
1298 managedP = managed_grips = (WidgetList)XtMalloc(alloc_size);
1299 unmanagedP = unmanaged_grips = (WidgetList)XtMalloc(alloc_size);
1301 ForAllChildren(pw, childP)
1302 if (IsPane(*childP) && HasGrip(*childP)) {
1303 if (XtIsManaged(*childP))
1304 *managedP++ = PaneInfo(*childP)->grip;
1306 *unmanagedP++ = PaneInfo(*childP)->grip;
1309 if (managedP != managed_grips) {
1310 *unmanagedP++ = *--managedP; /* Last grip is never managed */
1311 XtManageChildren(managed_grips, managedP - managed_grips);
1314 if (unmanagedP != unmanaged_grips)
1315 XtUnmanageChildren(unmanaged_grips, unmanagedP - unmanaged_grips);
1317 XtFree((char *)managed_grips);
1318 XtFree((char *)unmanaged_grips);
1326 * child - child that wants a grip to be created for it
1329 * Creates a grip widget.
1332 CreateGrip(Widget child)
1334 PanedWidget pw = (PanedWidget)XtParent(child);
1336 Cardinal num_args = 0;
1339 XtSetArg(arglist[num_args], XtNtranslations, pw->paned.grip_translations);
1341 if ((cursor = pw->paned.grip_cursor) == None) {
1343 cursor = pw->paned.v_grip_cursor;
1345 cursor = pw->paned.h_grip_cursor;
1348 XtSetArg(arglist[num_args], XtNcursor, cursor);
1350 PaneInfo(child)->grip = XtCreateWidget("grip", gripWidgetClass, (Widget)pw,
1353 XtAddCallback(PaneInfo(child)->grip, XtNcallback,
1354 HandleGrip, (XtPointer)child);
1367 PanedWidget pw = (PanedWidget)w;
1372 * Draw pane borders in internal border color
1374 values.foreground = pw->paned.internal_bp;
1375 valuemask = GCForeground;
1376 pw->paned.normgc = XtGetGC(w, valuemask, &values);
1379 * Erase pane borders with background color
1381 values.foreground = pw->core.background_pixel;
1382 valuemask = GCForeground;
1383 pw->paned.invgc = XtGetGC(w, valuemask, &values);
1386 * Draw Track lines (animate pane borders) in
1387 * internal border color ^ bg color
1389 values.function = GXinvert;
1390 values.plane_mask = pw->paned.internal_bp ^ pw->core.background_pixel;
1391 values.subwindow_mode = IncludeInferiors;
1392 valuemask = GCPlaneMask | GCFunction | GCSubwindowMode;
1393 pw->paned.flipgc = XtGetGC(w, valuemask, &values);
1398 * SetChildrenPrefSizes
1404 * Sets the preferred sizes of the children.
1407 SetChildrenPrefSizes(PanedWidget pw, unsigned int off_size)
1410 Boolean vert = IsVert(pw);
1411 XtWidgetGeometry request, reply;
1413 ForAllPanes(pw, childP)
1414 if (pw->paned.resize_children_to_pref || PaneInfo(*childP)->size == 0 ||
1415 PaneInfo(*childP)->resize_to_pref) {
1416 if (PaneInfo(*childP)->preferred_size != PANED_ASK_CHILD)
1417 PaneInfo(*childP)->wp_size = PaneInfo(*childP)->preferred_size;
1420 request.request_mode = CWWidth;
1421 request.width = off_size;
1424 request.request_mode = CWHeight;
1425 request.height = off_size;
1428 if ((XtQueryGeometry(*childP, &request, &reply)
1429 == XtGeometryAlmost)
1430 && (reply.request_mode = (vert ? CWHeight : CWWidth)))
1431 PaneInfo(*childP)->wp_size = GetRequestInfo(&reply, vert);
1433 PaneInfo(*childP)->wp_size = PaneSize(*childP, vert);
1436 PaneInfo(*childP)->size = PaneInfo(*childP)->wp_size;
1442 * ChangeAllGripCursors
1448 * Changes all the grip cursors.
1451 ChangeAllGripCursors(PanedWidget pw)
1455 ForAllPanes(pw, childP) {
1459 if ((cursor = pw->paned.grip_cursor) == None) {
1461 cursor = pw->paned.v_grip_cursor;
1463 cursor = pw->paned.h_grip_cursor;
1466 if (HasGrip(*childP)) {
1467 XtSetArg(arglist[0], XtNcursor, cursor);
1468 XtSetValues(PaneInfo(*childP)->grip, arglist, 1);
1479 * pane - pane that we are pushing
1482 * Pushes a value onto the pane stack.
1485 PushPaneStack(PanedWidget pw, Pane pane)
1487 PaneStack *stack = (PaneStack *)XtMalloc(sizeof(PaneStack));
1489 stack->next = pw->paned.stack;
1491 stack->start_size = pane->size;
1493 pw->paned.stack = stack;
1502 * shrink - True if we want to shrink this pane, False otherwise
1503 * pane - pane that we are popping (return)
1504 * start_size - size that this pane started at (return)
1507 * Gets the top value from the pane stack.
1510 GetPaneStack(PanedWidget pw, Bool shrink, Pane *pane, int *start_size)
1512 if (pw->paned.stack == NULL) {
1517 *pane = pw->paned.stack->pane;
1518 *start_size = pw->paned.stack->start_size;
1520 if (shrink != ((*pane)->size > *start_size))
1532 * Pops the top item off the pane stack.
1534 * Returns: True if this is not the last element on the stack
1537 PopPaneStack(PanedWidget pw)
1539 PaneStack *stack = pw->paned.stack;
1544 pw->paned.stack = stack->next;
1545 XtFree((char *)stack);
1547 if (pw->paned.stack == NULL)
1561 * Removes all entries from the pane stack.
1564 ClearPaneStack(PanedWidget pw)
1566 while(PopPaneStack(pw))
1571 XawPanedClassInitialize(void)
1573 XawInitializeWidgetSet();
1574 XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
1576 XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString,
1577 NULL, 0, XtCacheNone, NULL);
1580 /* The Geometry Manager only allows changes after Realize if
1581 * allow_resize is True in the constraints record.
1583 * For vertically paned widgets:
1585 * It only allows height changes, but offers the requested height
1586 * as a compromise if both width and height changes were requested.
1588 * For horizontal widgets the converse is true.
1589 * As all good Geometry Managers should, we will return No if the
1590 * request will have no effect; i.e. when the requestor is already
1591 * of the desired geometry.
1593 static XtGeometryResult
1594 XawPanedGeometryManager(Widget w, XtWidgetGeometry *request,
1595 XtWidgetGeometry *reply)
1597 PanedWidget pw = (PanedWidget)XtParent(w);
1598 XtGeometryMask mask = request->request_mode;
1599 Dimension old_size, old_wpsize, old_paned_size;
1600 Pane pane = PaneInfo(w);
1601 Boolean vert = IsVert(pw);
1602 Dimension on_size, off_size;
1603 XtGeometryResult result;
1604 Boolean almost = False;
1607 * If any of the following is true, disallow the geometry change
1609 * o The paned widget is realized and allow_resize is false for the pane
1610 * o The child did not ask to change the on_size
1611 * o The request is not a width or height request
1612 * o The requested size is the same as the current size
1615 if ((XtIsRealized((Widget)pw) && !pane->allow_resize)
1616 || !(mask & (vert ? CWHeight : CWWidth))
1617 ||(mask & ~(CWWidth | CWHeight))
1618 || GetRequestInfo(request, vert) == PaneSize(w, vert))
1619 return (XtGeometryNo);
1621 old_paned_size = PaneSize((Widget)pw, vert);
1622 old_wpsize = pane->wp_size;
1623 old_size = pane->size;
1625 pane->wp_size = pane->size = GetRequestInfo(request, vert);
1627 AdjustPanedSize(pw, PaneSize((Widget)pw, !vert), &result, &on_size,
1631 * Fool the Refigure Locations proc to thinking that we are
1632 * a different on_size
1635 if (result != XtGeometryNo) {
1637 XtHeight(pw) = on_size;
1639 XtWidth(pw) = on_size;
1642 RefigureLocations(pw, PaneIndex(w), AnyPane);
1645 * Set up reply struct and reset core on_size
1648 XtHeight(pw) = old_paned_size;
1649 reply->height = pane->size;
1650 reply->width = off_size;
1653 XtWidth(pw) = old_paned_size;
1654 reply->height = off_size;
1655 reply->width = pane->size;
1659 * IF either of the following is true
1661 * o There was a "off_size" request and the new "off_size" is different
1662 * from that requested
1663 * o There was no "off_size" request and the new "off_size" is different
1665 * o The "on_size" we will allow is different from that requested
1669 if (!((vert ? CWWidth : CWHeight) & mask)) {
1671 request->width = XtWidth(w);
1673 request->height = XtHeight(w);
1676 almost = GetRequestInfo(request, !vert) != GetRequestInfo(reply, !vert);
1677 almost |= (GetRequestInfo(request, vert) != GetRequestInfo(reply, vert));
1679 if ((mask & XtCWQueryOnly) || almost) {
1680 pane->wp_size = old_wpsize;
1681 pane->size = old_size;
1682 RefigureLocations(pw, PaneIndex(w), AnyPane);
1683 reply->request_mode = CWWidth | CWHeight;
1685 return (XtGeometryAlmost);
1688 AdjustPanedSize(pw, PaneSize((Widget) pw, !vert), NULL, NULL, NULL);
1689 CommitNewLocations(pw); /* layout already refigured */
1692 return (XtGeometryDone);
1697 XawPanedInitialize(Widget request, Widget cnew,
1698 ArgList args, Cardinal *num_args)
1700 PanedWidget pw = (PanedWidget)cnew;
1704 pw->paned.recursively_called = False;
1705 pw->paned.stack = NULL;
1706 pw->paned.resize_children_to_pref = True;
1707 pw->paned.num_panes = 0;
1711 XawPanedRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
1713 PanedWidget pw = (PanedWidget)w;
1716 if ((attributes->cursor = pw->paned.cursor) != None)
1717 *valueMask |= CWCursor;
1719 (*SuperClass->core_class.realize)(w, valueMask, attributes);
1722 * Before we commit the new locations we need to realize all the panes and
1725 ForAllPanes(pw, childP) {
1726 XtRealizeWidget(*childP);
1727 if (HasGrip(*childP))
1728 XtRealizeWidget(PaneInfo(*childP)->grip);
1731 RefigureLocationsAndCommit(w);
1732 pw->paned.resize_children_to_pref = False;
1736 XawPanedDestroy(Widget w)
1742 ReleaseGCs(Widget w)
1744 PanedWidget pw = (PanedWidget)w;
1746 XtReleaseGC(w, pw->paned.normgc);
1747 XtReleaseGC(w, pw->paned.invgc);
1748 XtReleaseGC(w, pw->paned.flipgc);
1752 XawPanedInsertChild(Widget w)
1754 Pane pane = PaneInfo(w);
1756 /* insert the child widget in the composite children list with the
1757 superclass insert_child routine
1759 (*SuperClass->composite_class.insert_child)(w);
1764 if (pane->show_grip == True) {
1766 if (pane->min == PANED_GRIP_SIZE)
1767 pane->min = PaneSize(pane->grip, IsVert((PanedWidget)XtParent(w)));
1770 if (pane->min == PANED_GRIP_SIZE)
1776 pane->paned_adjusted_me = False;
1780 XawPanedDeleteChild(Widget w)
1782 /* remove the subwidget info and destroy the grip */
1783 if (IsPane(w) && HasGrip(w))
1784 XtDestroyWidget(PaneInfo(w)->grip);
1786 /* delete the child widget in the composite children list with the
1787 superclass delete_child routine
1789 (*SuperClass->composite_class.delete_child)(w);
1793 XawPanedChangeManaged(Widget w)
1795 PanedWidget pw = (PanedWidget)w;
1796 Boolean vert = IsVert(pw);
1800 if (pw->paned.recursively_called++)
1804 * If the size is zero then set it to the size of the widest or tallest pane
1807 if ((size = PaneSize((Widget)pw, !vert)) == 0) {
1809 ForAllChildren(pw, childP)
1810 if (XtIsManaged(*childP) && (PaneSize(*childP, !vert) > size))
1811 size = PaneSize(*childP, !vert);
1814 ManageAndUnmanageGrips(pw);
1815 pw->paned.recursively_called = False;
1818 pw->paned.num_panes = 0;
1819 ForAllChildren(pw, childP)
1820 if (IsPane(*childP)) {
1821 if (XtIsManaged(*childP)) {
1822 Pane pane = PaneInfo(*childP);
1824 if (HasGrip(*childP))
1825 PaneInfo(pane->grip)->position = pw->paned.num_panes;
1826 pane->position = pw->paned.num_panes; /* TEMPORY -CDP 3/89 */
1827 pw->paned.num_panes++;
1830 break; /* This list is already sorted */
1833 SetChildrenPrefSizes((PanedWidget) w, size);
1836 * ForAllPanes can now be used
1838 if (PaneSize((Widget) pw, vert) == 0)
1839 AdjustPanedSize(pw, size, NULL, NULL, NULL);
1841 if (XtIsRealized((Widget)pw))
1842 RefigureLocationsAndCommit((Widget)pw);
1846 XawPanedResize(Widget w)
1848 SetChildrenPrefSizes((PanedWidget)w,
1849 PaneSize(w, !IsVert((PanedWidget)w)));
1850 RefigureLocationsAndCommit(w);
1855 XawPanedRedisplay(Widget w, XEvent *event, Region region)
1857 DrawInternalBorders((PanedWidget)w);
1862 XawPanedSetValues(Widget old, Widget request, Widget cnew,
1863 ArgList args, Cardinal *num_args)
1865 PanedWidget old_pw = (PanedWidget)old;
1866 PanedWidget new_pw = (PanedWidget)cnew;
1867 Boolean redisplay = False;
1869 if ((old_pw->paned.cursor != new_pw->paned.cursor) && XtIsRealized(cnew))
1870 XDefineCursor(XtDisplay(cnew), XtWindow(cnew), new_pw->paned.cursor);
1872 if (old_pw->paned.internal_bp != new_pw->paned.internal_bp ||
1873 old_pw->core.background_pixel != new_pw->core.background_pixel) {
1879 if (old_pw->paned.grip_cursor != new_pw->paned.grip_cursor ||
1880 old_pw->paned.v_grip_cursor != new_pw->paned.v_grip_cursor ||
1881 old_pw->paned.h_grip_cursor != new_pw->paned.h_grip_cursor)
1882 ChangeAllGripCursors(new_pw);
1884 if (IsVert(old_pw) != IsVert(new_pw)) {
1886 * We are fooling the paned widget into thinking that is needs to
1887 * fully refigure everything, which is what we want
1890 XtWidth(new_pw) = 0;
1892 XtHeight(new_pw) = 0;
1894 new_pw->paned.resize_children_to_pref = True;
1895 XawPanedChangeManaged(cnew); /* Seems weird, but does the right thing */
1896 new_pw->paned.resize_children_to_pref = False;
1897 if (new_pw->paned.grip_cursor == None)
1898 ChangeAllGripCursors(new_pw);
1902 if (old_pw->paned.internal_bw != new_pw->paned.internal_bw) {
1903 AdjustPanedSize(new_pw, PaneSize(cnew, !IsVert(old_pw)),
1905 RefigureLocationsAndCommit(cnew);
1906 return (True); /* We have done a full configuration, return */
1909 if (old_pw->paned.grip_indent != new_pw->paned.grip_indent &&
1910 XtIsRealized(cnew)) {
1911 CommitNewLocations(new_pw);
1920 XawPanedPaneSetValues(Widget old, Widget request, Widget cnew,
1921 ArgList args, Cardinal *num_args)
1923 Pane old_pane = PaneInfo(old);
1924 Pane new_pane = PaneInfo(cnew);
1925 Boolean redisplay = False;
1927 /* Check for new min and max */
1928 if (old_pane->min != new_pane->min || old_pane->max != new_pane->max)
1929 XawPanedSetMinMax(cnew, (int)new_pane->min, (int)new_pane->max);
1931 /* Check for change in XtNshowGrip */
1932 if (old_pane->show_grip != new_pane->show_grip) {
1933 if (new_pane->show_grip == True) {
1935 if (XtIsRealized(XtParent(cnew))) {
1936 if (XtIsManaged(cnew)) /* if paned is unrealized this will
1937 happen automatically at realize time
1939 XtManageChild(PaneInfo(cnew)->grip); /* manage the grip */
1940 XtRealizeWidget(PaneInfo(cnew)->grip); /* realize the grip */
1941 CommitNewLocations((PanedWidget)XtParent(cnew));
1944 else if (HasGrip(old)) {
1945 XtDestroyWidget(old_pane->grip);
1946 new_pane->grip = NULL;
1962 * widget - widget that is a child of the Paned widget
1963 * min - new min and max size for the pane
1967 * Sets the min and max size for a pane.
1970 XawPanedSetMinMax(Widget widget, int min, int max)
1972 Pane pane = PaneInfo(widget);
1976 RefigureLocationsAndCommit(widget->core.parent);
1984 * widget - widget that is a child of the Paned widget
1985 * min - current min and max size for the pane (return)
1989 * Gets the min and max size for a pane.
1992 XawPanedGetMinMax(Widget widget, int *min, int *max)
1994 Pane pane = PaneInfo(widget);
2002 * XawPanedSetRefigureMode
2006 * mode - if False then inhibit refigure
2009 * Allows a flag to be set the will inhibit
2010 * the paned widgets relayout routine.
2013 XawPanedSetRefigureMode(Widget w,
2014 #if NeedWidePrototypes
2021 ((PanedWidget)w)->paned.refiguremode = mode;
2022 RefigureLocationsAndCommit(w);
2033 * Returns the number of panes in the paned widget.
2035 * the number of panes in the paned widget
2038 XawPanedGetNumSub(Widget w)
2040 return (((PanedWidget)w)->paned.num_panes);
2045 * XawPanedAllowResize
2048 * widget - child of the paned widget
2051 * Allows a flag to be set that determines if the paned
2052 * widget will allow geometry requests from this child.
2055 XawPanedAllowResize(Widget widget,
2056 #if NeedWidePrototypes
2059 Boolean allow_resize
2063 PaneInfo(widget)->allow_resize = allow_resize;