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