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