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