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