Paned.c revision 5b16253f
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};
470
471WidgetClass panedWidgetClass = (WidgetClass)&panedClassRec;
472WidgetClass vPanedWidgetClass = (WidgetClass)&panedClassRec;
473
474/*
475 * Implementation
476 */
477/* Function:
478 *	AdjustPanedSize
479 *
480 * Parameters:
481 *	pw	     - paned widget to adjust
482 *	off_size     - new off_size to use
483 *	result_ret   - result of query (return)
484 *	on_size_ret  - new on_size  (return)
485 *	off_size_ret - new off_size (return)
486 *
487 * Description:
488 *	Adjusts the size of the pane.
489 *
490 * Returns:
491 *	amount of change in size
492 */
493static void
494AdjustPanedSize(PanedWidget pw, unsigned int off_size,
495		XtGeometryResult *result_ret,
496		Dimension *on_size_ret, Dimension *off_size_ret)
497{
498    Dimension old_size = PaneSize((Widget)pw, IsVert(pw));
499    Dimension newsize = 0;
500    Widget *childP;
501    XtWidgetGeometry request, reply;
502
503    request.request_mode = CWWidth | CWHeight;
504
505    ForAllPanes(pw, childP) {
506	int size = Max(PaneInfo(*childP)->size, (int)PaneInfo(*childP)->min);
507
508	AssignMin(size, (int)PaneInfo(*childP)->max);
509	newsize = (newsize + (size + pw->paned.internal_bw));
510    }
511    newsize = (Dimension)(newsize - pw->paned.internal_bw);
512
513    if (newsize < 1)
514	newsize = 1;
515
516    if (IsVert(pw)) {
517	request.width = (Dimension)off_size;
518	request.height = newsize;
519    }
520    else {
521	request.width = newsize;
522	request.height = (Dimension)off_size;
523    }
524
525    if (result_ret != NULL) {
526	request.request_mode |= XtCWQueryOnly;
527
528	*result_ret = XtMakeGeometryRequest((Widget)pw, &request, &reply);
529	_XawImCallVendorShellExtResize((Widget)pw);
530
531	if (newsize == old_size || *result_ret == XtGeometryNo) {
532	    *on_size_ret = old_size;
533	    *off_size_ret = (Dimension)off_size;
534	    return;
535	}
536	if (*result_ret != XtGeometryAlmost) {
537	    *on_size_ret = GetRequestInfo(&request, IsVert(pw));
538	    *off_size_ret = GetRequestInfo(&request, !IsVert(pw));
539	    return;
540	}
541	*on_size_ret = GetRequestInfo(&reply, IsVert(pw));
542	*off_size_ret = GetRequestInfo(&reply, !IsVert(pw));
543	return;
544    }
545
546    if (newsize == old_size)
547	return;
548
549    if (XtMakeGeometryRequest((Widget)pw, &request, &reply) == XtGeometryAlmost)
550	XtMakeGeometryRequest((Widget)pw, &reply, &request);
551}
552
553/*
554 * Function:
555 *	ChoosePaneToResize.
556 *
557 * Parameters:
558 *	pw	  - paned widget
559 *	paneindex - index of the current pane
560 *	dir	  - direction to search first
561 *	shrink	  - True if we need to shrink a pane, False otherwise
562 *
563 * Description:
564 *	  This function chooses a pane to resize.
565 *	  They are chosen using the following rules:
566 *
567 *		   1) size < max && size > min
568 *	2) skip adjust == False
569 *	3) widget not its preferred height
570 *	   && this change will bring it closer
571 *	   && The user has not resized this pane.
572 *
573 *		   If no widgets are found that fits all the rules then
574 *		      rule #3 is broken.
575 *		   If there are still no widgets found than
576 *		      rule #2 is broken.
577 *		   Rule #1 is never broken.
578 *		   If no widgets are found then NULL is returned.
579 *
580 * Returns:
581 *	pane to resize or NULL
582 */
583static Pane
584ChoosePaneToResize(PanedWidget pw, int paneindex, Direction dir, Bool shrink)
585{
586    Widget *childP;
587    int rules = 3;
588    Direction _dir = dir;
589    int _index = paneindex;
590
591    if (paneindex == NO_INDEX || dir == AnyPane) {		/* Use defaults */
592	_dir = LowRightPane;			/* Go up - really */
593	_index = pw->paned.num_panes - 1;	/* Start the last pane, and work
594						   backwards */
595    }
596    childP = pw->composite.children + _index;
597
598    /*CONSTCOND*/
599    while(True) {
600	Pane pane = PaneInfo(*childP);
601
602	if ((rules < 3 || SatisfiesRule3(pane, shrink))
603	    && (rules < 2 || SatisfiesRule2(pane))
604	    && SatisfiesRule1(pane, shrink)
605	    && (paneindex != PaneIndex(*childP) || dir == AnyPane))
606	    return (pane);
607
608	/*
609	 * This is counter-intuitive, but if we are resizing the pane
610	 * above the grip we want to choose a pane below the grip to lose,
611	 * and visa-versa
612	 */
613	if (_dir == LowRightPane)
614	    --childP;
615	else
616	    ++childP;
617
618	/*
619	 * If we have come to and edge then reduce the rule set, and try again
620	 * If we are reduced the rules to none, then return NULL
621	 */
622	if ((childP - pw->composite.children) < 0 ||
623	    (childP - pw->composite.children) >= pw->paned.num_panes) {
624	    if (--rules < 1)	/* less strict rules */
625		return (NULL);
626	    childP = pw->composite.children + _index;
627	}
628    }
629}
630
631/*
632 * Function:
633 *	LoopAndRefigureChildren
634 *
635 * Parameters:
636 *	pw	  - paned widget
637 *	paneindex - number of the pane border we are moving
638 *	dir	  - pane to move (either UpLeftPane or LowRightPane)
639 *	sizeused  - current amount of space used (used and returned)
640 *
641 * Description:
642 *	  If we are resizing either the UpleftPane or LowRight Pane loop
643 *	through all the children to see if any will allow us to resize them.
644 */
645static void
646LoopAndRefigureChildren(PanedWidget pw, int paneindex, Direction dir,
647			int *sizeused)
648{
649    int pane_size = (int)PaneSize((Widget)pw, IsVert(pw));
650    Boolean shrink = (*sizeused > pane_size);
651
652    if (dir == LowRightPane)
653	paneindex++;
654
655    /* While all panes do not fit properly */
656    while (*sizeused != pane_size) {
657	/*
658	 * Choose a pane to resize
659	 * First look on the Pane Stack, and then go hunting for another one
660	 * If we fail to find a pane to resize then give up
661	 */
662	Pane pane;
663	int start_size;
664	Dimension old;
665	Boolean rule3_ok = False, from_stack = True;
666
667	GetPaneStack(pw, shrink, &pane, &start_size);
668	if (pane == NULL) {
669	    pane = ChoosePaneToResize(pw, paneindex, dir, shrink);
670	    if (pane == NULL)
671		return; /* no one to resize, give up */
672
673	    rule3_ok = SatisfiesRule3(pane, shrink);
674	    from_stack = False;
675	    PushPaneStack(pw, pane);
676	}
677
678	/*
679	 * Try to resize this pane so that all panes will fit, take min and max
680	 * into account
681	 */
682	old = (Dimension) pane->size;
683	pane->size += pane_size - *sizeused;
684
685	if (from_stack) {
686	    if (shrink) {
687		AssignMax(pane->size, start_size);
688	    }	/* don't remove these braces */
689	    else
690		AssignMin(pane->size, start_size);
691
692	  if (pane->size == start_size)
693	    (void)PopPaneStack(pw);
694	}
695	else if (rule3_ok) {
696	    if (shrink) {
697		AssignMax(pane->size, (int)pane->wp_size);
698	    }	/* don't remove these braces */
699	    else
700		AssignMin(pane->size, (int)pane->wp_size);
701	}
702
703	pane->paned_adjusted_me = pane->size != pane->wp_size;
704	AssignMax(pane->size, (int)pane->min);
705	AssignMin(pane->size, (int)pane->max);
706	*sizeused += (pane->size - old);
707    }
708}
709
710/*
711 * Function:
712 *	RefigureLocations
713 *
714 * Parameters:
715 *	pw	  - paned widget
716 *	paneindex - child to start refiguring at
717 *	dir	  - direction to move from child
718 *
719 * Description:
720 *	  Refigures all locations of children.
721 *      There are special arguments to paneindex and dir, they are:
722 *      paneindex - NO_INDEX.
723 *      dir   - AnyPane.
724 *
725 *      If either of these is true then all panes may be resized and
726 *      the choosing of panes proceeds in reverse order starting with the
727 *      last child.
728 */
729static void
730RefigureLocations(PanedWidget pw, int paneindex, Direction dir)
731{
732    Widget *childP;
733    int pane_size = (int)PaneSize((Widget)pw, IsVert(pw));
734    int sizeused = 0;
735    Position loc = 0;
736
737    if (pw->paned.num_panes == 0 || !pw->paned.refiguremode)
738	return;
739
740    /*
741     * Get an initial estimate of the size we will use
742     */
743    ForAllPanes(pw, childP) {
744	Pane pane = PaneInfo(*childP);
745
746	AssignMax(pane->size, (int) pane->min);
747	AssignMin(pane->size, (int) pane->max);
748	sizeused += (int)pane->size + (int)pw->paned.internal_bw;
749    }
750    sizeused -= (int)pw->paned.internal_bw;
751
752    if (dir != ThisBorderOnly && sizeused != pane_size)
753	LoopAndRefigureChildren(pw, paneindex, dir, &sizeused);
754
755    /*
756     * If we still are not the right size, then tell the pane that
757     * wanted to resize that it can't
758     */
759    if (paneindex != NO_INDEX && dir != AnyPane) {
760	Pane pane = PaneInfo(*(pw->composite.children + paneindex));
761	Dimension old = (Dimension)pane->size;
762
763	pane->size += pane_size - sizeused;
764	AssignMax(pane->size, (int) pane->min);
765	AssignMin(pane->size, (int) pane->max);
766	sizeused += pane->size - old;
767    }
768
769    /*
770     * It is possible that the panes will not fit inside the vpaned widget, but
771     * we have tried out best
772     *
773     * Assign each pane a location
774     */
775    ForAllPanes(pw, childP) {
776	PaneInfo(*childP)->delta = loc;
777	loc = (Position)(loc + (PaneInfo(*childP)->size + pw->paned.internal_bw));
778    }
779}
780
781/*
782 * Function:
783 *	CommitNewLocations
784 *
785 * Parameters:
786 *	pw - paned widget
787 *
788 * Description:
789 *	Commits all of the previously figured locations.
790 */
791static void
792CommitNewLocations(PanedWidget pw)
793{
794    Widget *childP;
795    XWindowChanges changes;
796
797    changes.stack_mode = Above;
798
799    ForAllPanes(pw, childP) {
800	Pane pane = PaneInfo(*childP);
801	Widget grip = pane->grip;	/* may be NULL */
802
803	if (IsVert(pw)) {
804	    XtMoveWidget(*childP, (Position) 0, pane->delta);
805	    XtResizeWidget(*childP, XtWidth(pw), (Dimension)pane->size, 0);
806
807	    if (HasGrip(*childP)) {	/* Move and Display the Grip */
808		changes.x = XtWidth(pw) - pw->paned.grip_indent -
809			    XtWidth(grip) - (XtBorderWidth(grip) << 1);
810		changes.y = XtY(*childP) + XtHeight(*childP) -
811			    (XtHeight(grip) >> 1) - XtBorderWidth(grip) +
812			    (pw->paned.internal_bw >> 1);
813	    }
814	}
815	else {
816	    XtMoveWidget(*childP, pane->delta, 0);
817	    XtResizeWidget(*childP, (Dimension)pane->size, (Dimension)XtHeight(pw), 0);
818
819	    if (HasGrip(*childP)) {		/* Move and Display the Grip */
820		changes.x = XtX(*childP) + XtWidth(*childP) -
821			    (XtWidth(grip) >> 1) - XtBorderWidth(grip) +
822			    (pw->paned.internal_bw >> 1);
823		changes.y = XtHeight(pw) - pw->paned.grip_indent -
824			    XtHeight(grip) - (XtBorderWidth(grip) << 1);
825	    }
826	}
827
828	/*
829	 * This should match XtMoveWidget, except that we're also insuring the
830	 * grip is Raised in the same request
831	 */
832
833	if (HasGrip(*childP)) {
834	    XtX(grip) = (Position)changes.x;
835	    XtY(grip) = (Position)changes.y;
836
837	    if (XtIsRealized(pane->grip))
838		XConfigureWindow(XtDisplay(pane->grip), XtWindow(pane->grip),
839				 CWX | CWY | CWStackMode, &changes);
840	}
841    }
842    ClearPaneStack(pw);
843}
844
845/*
846 * Function:
847 *	RefigureLocationsAndCommit
848 *
849 * Parameters:
850 *	pw - paned widget
851 *
852 * Description:
853 *	Refigures all locations in a paned widget and commits them immediately.
854 *
855 *      This function does nothing if any of the following are true.
856 *      o refiguremode is false.
857 *      o The widget is unrealized.
858 *      o There are no panes is the paned widget.
859 */
860static void
861RefigureLocationsAndCommit(Widget w)
862{
863    PanedWidget pw = (PanedWidget)w;
864
865    if (pw->paned.refiguremode && XtIsRealized(w) && pw->paned.num_panes > 0) {
866	RefigureLocations(pw, NO_INDEX, AnyPane);
867	CommitNewLocations(pw);
868    }
869}
870
871/*
872 * Function:
873 *	_DrawRect
874 *
875 * Parameters:
876 *	pw	 - paned widget
877 *	gc	 - gc to used for the draw
878 *	on_olc	 - location of upper left corner of rect
879 *	off_loc	 - ""
880 *	on_size	 - size of rectangle
881 *	off_size - ""
882 *
883 * Description:
884 *	Draws a rectangle in the proper orientation.
885 */
886static void
887_DrawRect(PanedWidget pw, GC gc, int on_loc, int off_loc,
888	  unsigned int on_size, unsigned int off_size)
889{
890    if (IsVert(pw))
891	XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc,
892		       off_loc, on_loc, off_size, on_size);
893    else
894	XFillRectangle(XtDisplay((Widget)pw), XtWindow((Widget)pw), gc,
895		       on_loc, off_loc, on_size, off_size);
896}
897
898/*
899 * Function:
900 *	_DrawInternalBorders
901 *
902 * Parameters:
903 *	pw - paned widget
904 *	gc - GC to use to draw the borders
905 *
906 * Description:
907 *	Draws the internal borders into the paned widget.
908 */
909static void
910_DrawInternalBorders(PanedWidget pw, GC gc)
911{
912    Widget *childP;
913    int on_loc, off_loc;
914    unsigned int on_size, off_size;
915
916    /*
917     * This is an optimization.  Do not paint the internal borders if
918     * they are the same color as the background
919     */
920    if (pw->core.background_pixel == pw->paned.internal_bp)
921	return;
922
923    off_loc = 0;
924    off_size = (unsigned int) PaneSize((Widget)pw, !IsVert(pw));
925    on_size = (unsigned int)pw->paned.internal_bw;
926
927    ForAllPanes(pw, childP) {
928	on_loc = IsVert(pw) ? XtY(*childP) : XtX(*childP);
929	on_loc -= (int)on_size;
930
931	_DrawRect(pw, gc, on_loc, off_loc, on_size, off_size);
932    }
933}
934
935#define DrawInternalBorders(pw)				\
936	_DrawInternalBorders((pw), (pw)->paned.normgc)
937#define EraseInternalBorders(pw)			\
938	_DrawInternalBorders((pw), (pw)->paned.invgc)
939/*
940 * Function Name:
941 *	_DrawTrackLines
942 *
943 * Parameters:
944 *	pw - Paned widget
945 *	erase - if True then just erase track lines, else draw them in
946 *
947 * Description:
948 *	Draws the lines that animate the pane borders when the grips are moved.
949 */
950static void
951_DrawTrackLines(PanedWidget pw, Bool erase)
952{
953    Widget *childP;
954    Pane pane;
955    int on_loc, off_loc;
956    unsigned int on_size, off_size;
957
958    off_loc = 0;
959    off_size = PaneSize((Widget)pw, !IsVert(pw));
960
961    ForAllPanes(pw, childP) {
962	pane = PaneInfo(*childP);
963	if (erase || pane->olddelta != pane->delta) {
964	    on_size = pw->paned.internal_bw;
965	    if (!erase) {
966		on_loc = PaneInfo(*childP)->olddelta - (int) on_size;
967		_DrawRect(pw, pw->paned.flipgc,
968			  on_loc, off_loc, on_size, off_size);
969	    }
970
971	    on_loc = PaneInfo(*childP)->delta - (int)on_size;
972
973	    _DrawRect(pw, pw->paned.flipgc,
974		      on_loc, off_loc, on_size, off_size);
975
976	    pane->olddelta = pane->delta;
977	}
978    }
979}
980
981#define DrawTrackLines(pw)	_DrawTrackLines((pw), False);
982#define EraseTrackLines(pw)	_DrawTrackLines((pw), True);
983/*
984 * Function:
985 *	GetEventLocation
986 *
987 * Parameters:
988 *	pw    - the paned widget
989 *	event - pointer to an event
990 *
991 * Description:
992 *	Converts and event to an x and y location.
993 *
994 * Returns:
995 *	if this is a vertical pane then (y) else (x)
996 */
997static int
998GetEventLocation(PanedWidget pw, XEvent *event)
999{
1000    int x, y;
1001
1002    switch (event->xany.type) {
1003	case ButtonPress:
1004	case ButtonRelease:
1005	    x = event->xbutton.x_root;
1006	    y = event->xbutton.y_root;
1007	    break;
1008	case KeyPress:
1009	case KeyRelease:
1010	    x = event->xkey.x_root;
1011	    y = event->xkey.y_root;
1012	    break;
1013	case MotionNotify:
1014	    x = event->xmotion.x_root;
1015	    y = event->xmotion.y_root;
1016	    break;
1017	default:
1018	    x = pw->paned.start_loc;
1019	    y = pw->paned.start_loc;
1020    }
1021
1022    if (IsVert(pw))
1023	return (y);
1024
1025  return (x);
1026}
1027
1028/*
1029 * Function:
1030 *	StartGripAdjustment
1031 *
1032 * Parameters:
1033 *	pw   - paned widget
1034 *	grip - grip widget selected
1035 *	dir  - direction that we are to be moving
1036 *
1037 * Description:
1038 *	Starts the grip adjustment procedure.
1039 */
1040static void
1041StartGripAdjustment(PanedWidget pw, Widget grip, Direction dir)
1042{
1043    Widget *childP;
1044    Cursor cursor;
1045
1046    pw->paned.whichadd = pw->paned.whichsub = NULL;
1047
1048    if (dir == ThisBorderOnly || dir == UpLeftPane)
1049	pw->paned.whichadd = pw->composite.children[PaneIndex(grip)];
1050    if (dir == ThisBorderOnly || dir == LowRightPane)
1051	pw->paned.whichsub = pw->composite.children[PaneIndex(grip) + 1];
1052
1053    /*
1054     * Change the cursor
1055     */
1056    if (XtIsRealized(grip)) {
1057	if (IsVert(pw)) {
1058	    if (dir == UpLeftPane)
1059		cursor = pw->paned.adjust_upper_cursor;
1060	    else if (dir == LowRightPane)
1061		cursor = pw->paned.adjust_lower_cursor;
1062	    else {
1063		if (pw->paned.adjust_this_cursor == None)
1064		    cursor = pw->paned.v_adjust_this_cursor;
1065		else
1066		    cursor = pw->paned.adjust_this_cursor;
1067	    }
1068	}
1069	else {
1070	    if (dir == UpLeftPane)
1071		cursor = pw->paned.adjust_left_cursor;
1072	    else if (dir == LowRightPane)
1073		cursor = pw->paned.adjust_right_cursor;
1074	    else {
1075		if (pw->paned.adjust_this_cursor == None)
1076		    cursor = pw->paned.h_adjust_this_cursor;
1077		else
1078		    cursor = pw->paned.adjust_this_cursor;
1079	    }
1080	}
1081
1082	XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor);
1083    }
1084
1085    EraseInternalBorders(pw);
1086    ForAllPanes(pw, childP)
1087	PaneInfo(*childP)->olddelta = -99;
1088
1089    EraseTrackLines(pw);
1090}
1091
1092/*
1093 * Function:
1094 *	MoveGripAdjustment
1095 *
1096 * Parameters:
1097 *	pw   - paned widget
1098 *	grip - grip that we are moving
1099 *	dir  - direction the pane we are interested is w.r.t the grip
1100 *	loc  - location of pointer in proper direction
1101 *
1102 * Description:
1103 *	This routine moves all panes around when a grip is moved.
1104 */
1105static void
1106MoveGripAdjustment(PanedWidget pw, Widget grip, Direction dir, int loc)
1107{
1108    int diff, add_size = 0, sub_size = 0;
1109
1110    diff = loc - pw->paned.start_loc;
1111
1112    if (pw->paned.whichadd)
1113    add_size = PaneSize(pw->paned.whichadd, IsVert(pw)) + diff;
1114
1115    if (pw->paned.whichsub)
1116    sub_size = PaneSize(pw->paned.whichsub, IsVert(pw)) - diff;
1117
1118    /*
1119     * If moving this border only then do not allow either of the borders
1120     * to go beyond the min or max size allowed
1121     */
1122    if (dir == ThisBorderOnly) {
1123	int old_add_size = add_size, old_sub_size;
1124
1125	AssignMax(add_size, (int)PaneInfo(pw->paned.whichadd)->min);
1126	AssignMin(add_size, (int)PaneInfo(pw->paned.whichadd)->max);
1127	if (add_size != old_add_size)
1128	    sub_size += old_add_size - add_size;
1129
1130	old_sub_size = sub_size;
1131	AssignMax(sub_size, (int)PaneInfo(pw->paned.whichsub)->min);
1132	AssignMin(sub_size, (int)PaneInfo(pw->paned.whichsub)->max);
1133	if (sub_size != old_sub_size)
1134	    return;	/* Abort to current sizes */
1135    }
1136
1137    if (add_size != 0)
1138	PaneInfo(pw->paned.whichadd)->size = add_size;
1139    if (sub_size != 0)
1140	PaneInfo(pw->paned.whichsub)->size = sub_size;
1141    RefigureLocations(pw, PaneIndex(grip), dir);
1142    DrawTrackLines(pw);
1143}
1144
1145/*
1146 * Function:
1147 *	CommitGripAdjustment
1148 *
1149 * Parameters:
1150 *	pw - paned widget
1151 *
1152 * Description:
1153 *	Commits the grip adjustment.
1154 */
1155static void
1156CommitGripAdjustment(PanedWidget pw)
1157{
1158    EraseTrackLines(pw);
1159    CommitNewLocations(pw);
1160    DrawInternalBorders(pw);
1161
1162    /*
1163     * Since the user selected this size then use it as the preferred size
1164     */
1165    if (pw->paned.whichadd) {
1166	Pane pane = PaneInfo(pw->paned.whichadd);
1167
1168	pane->wp_size = (Dimension)pane->size;
1169    }
1170    if (pw->paned.whichsub) {
1171	Pane pane = PaneInfo(pw->paned.whichsub);
1172
1173	pane->wp_size = (Dimension)pane->size;
1174    }
1175}
1176
1177/*
1178 * Function:
1179 *	HandleGrip
1180 *
1181 * Parameters:
1182 *	grip	  - grip widget that has been moved
1183 *	temp	  - (not used)
1184 *	call_data - data passed to us from the grip widget
1185 *
1186 * Description:
1187 *	Handles the grip manipulations.
1188 */
1189/*ARGSUSED*/
1190static void
1191HandleGrip(Widget grip, XtPointer temp _X_UNUSED, XtPointer callData)
1192{
1193    XawGripCallData call_data = (XawGripCallData)callData;
1194    PanedWidget pw = (PanedWidget) XtParent(grip);
1195    int loc;
1196    char action_type[2], direction[2];
1197    Cursor cursor;
1198    Arg arglist[1];
1199
1200    if (call_data->num_params)
1201	XmuNCopyISOLatin1Uppered(action_type, call_data->params[0],
1202				 sizeof(action_type));
1203
1204    if (call_data->num_params == 0
1205	|| (action_type[0] == 'C' && call_data->num_params != 1)
1206	|| (action_type[0] != 'C' && call_data->num_params != 2))
1207	XtAppError(XtWidgetToApplicationContext(grip),
1208		   "Paned GripAction has been passed incorrect parameters.");
1209
1210    loc = GetEventLocation(pw, (XEvent *)call_data->event);
1211
1212    if (action_type[0] != 'C')
1213	XmuNCopyISOLatin1Uppered(direction, call_data->params[1],
1214				 sizeof(direction));
1215
1216    switch (action_type[0]) {
1217	case 'S':		/* Start adjustment */
1218	    pw->paned.resize_children_to_pref = False;
1219	    StartGripAdjustment(pw, grip, (Direction)direction[0]);
1220	    pw->paned.start_loc = loc;
1221	    break;
1222	case 'M':
1223	    MoveGripAdjustment(pw, grip, (Direction)direction[0], loc);
1224	    break;
1225	case 'C':
1226	    XtSetArg(arglist[0], XtNcursor, &cursor);
1227	    XtGetValues(grip, arglist, 1);
1228	    XDefineCursor(XtDisplay(grip), XtWindow(grip), cursor);
1229	    CommitGripAdjustment(pw);
1230	    break;
1231	default:
1232	    XtAppError(XtWidgetToApplicationContext(grip),
1233		       "Paned GripAction(); 1st parameter invalid");
1234	    break;
1235     }
1236}
1237
1238/*
1239 * Function:
1240 *	ResortChildren
1241 *
1242 * Arguments:
1243 *	pw - paned widget
1244 *
1245 * Description:
1246 *	Resorts the children so that all managed children are first.
1247 */
1248static void
1249ResortChildren(PanedWidget pw)
1250{
1251    Widget *unmanagedP, *childP;
1252
1253    unmanagedP = NULL;
1254    ForAllChildren(pw, childP) {
1255	if (!IsPane(*childP) || !XtIsManaged(*childP)) {
1256	    /*
1257	     * We only keep track of the first unmanaged pane
1258	     */
1259	    if (unmanagedP == NULL)
1260		unmanagedP = childP;
1261	}
1262	else {			/* must be a managed pane */
1263	    /*
1264	     * If an earlier widget was not a managed pane, then swap
1265	     */
1266	    if (unmanagedP != NULL) {
1267		Widget child = *unmanagedP;
1268
1269		*unmanagedP = *childP;
1270		*childP = child;
1271		childP = unmanagedP;  /* easiest to just back-track */
1272		unmanagedP = NULL;    /* in case there is another managed */
1273	   }
1274       }
1275   }
1276}
1277
1278/*
1279 * Function:
1280 *	ManageAndUnmanageGrips
1281 *
1282 * Parameters:
1283 *	pw - paned widget
1284 *
1285 * Description:
1286 *	  This function manages and unmanages the grips so that
1287 *		   the managed state of each grip matches that of its pane.
1288 */
1289static void
1290ManageAndUnmanageGrips(PanedWidget pw)
1291{
1292    WidgetList managed_grips, unmanaged_grips;
1293    Widget *managedP, *unmanagedP, *childP;
1294    Cardinal alloc_size;
1295
1296    alloc_size = (Cardinal)(sizeof(Widget) * (pw->composite.num_children >> 1));
1297    managedP = managed_grips = (WidgetList)XtMalloc(alloc_size);
1298    unmanagedP = unmanaged_grips = (WidgetList)XtMalloc(alloc_size);
1299
1300    ForAllChildren(pw, childP)
1301	if (IsPane(*childP) && HasGrip(*childP)) {
1302	    if (XtIsManaged(*childP))
1303		*managedP++ = PaneInfo(*childP)->grip;
1304	    else
1305		*unmanagedP++ = PaneInfo(*childP)->grip;
1306	}
1307
1308    if (managedP != managed_grips) {
1309	*unmanagedP++ = *--managedP;   /* Last grip is never managed */
1310	XtManageChildren(managed_grips, (Cardinal)(managedP - managed_grips));
1311    }
1312
1313    if (unmanagedP != unmanaged_grips)
1314	XtUnmanageChildren(unmanaged_grips, (Cardinal)(unmanagedP - unmanaged_grips));
1315
1316    XtFree((char *)managed_grips);
1317    XtFree((char *)unmanaged_grips);
1318}
1319
1320/*
1321 * Function:
1322 *	CreateGrip
1323 *
1324 * Parameters:
1325 *	child - child that wants a grip to be created for it
1326 *
1327 * Description:
1328 *	Creates a grip widget.
1329 */
1330static void
1331CreateGrip(Widget child)
1332{
1333    PanedWidget pw = (PanedWidget)XtParent(child);
1334    Arg arglist[2];
1335    Cardinal num_args = 0;
1336    Cursor cursor;
1337
1338    XtSetArg(arglist[num_args], XtNtranslations, pw->paned.grip_translations);
1339    num_args++;
1340    if ((cursor = pw->paned.grip_cursor) == None) {
1341	if (IsVert(pw))
1342	    cursor = pw->paned.v_grip_cursor;
1343	else
1344	    cursor = pw->paned.h_grip_cursor;
1345    }
1346
1347    XtSetArg(arglist[num_args], XtNcursor, cursor);
1348    num_args++;
1349    PaneInfo(child)->grip = XtCreateWidget("grip", gripWidgetClass, (Widget)pw,
1350					   arglist, num_args);
1351
1352    XtAddCallback(PaneInfo(child)->grip, XtNcallback,
1353		  HandleGrip, (XtPointer)child);
1354}
1355
1356/*
1357 * Function:
1358 *	GetGCs
1359 *
1360 * Parameters:
1361 *	w - paned widget
1362 */
1363static void
1364GetGCs(Widget w)
1365{
1366    PanedWidget pw = (PanedWidget)w;
1367    XtGCMask valuemask;
1368    XGCValues values;
1369
1370    /*
1371     * Draw pane borders in internal border color
1372     */
1373    values.foreground = pw->paned.internal_bp;
1374    valuemask = GCForeground;
1375    pw->paned.normgc = XtGetGC(w, valuemask, &values);
1376
1377    /*
1378     * Erase pane borders with background color
1379     */
1380    values.foreground = pw->core.background_pixel;
1381    valuemask = GCForeground;
1382    pw->paned.invgc = XtGetGC(w, valuemask, &values);
1383
1384    /*
1385     * Draw Track lines (animate pane borders) in
1386     * internal border color ^ bg color
1387     */
1388    values.function = GXinvert;
1389    values.plane_mask = pw->paned.internal_bp ^ pw->core.background_pixel;
1390    values.subwindow_mode = IncludeInferiors;
1391    valuemask = GCPlaneMask | GCFunction | GCSubwindowMode;
1392    pw->paned.flipgc = XtGetGC(w, valuemask, &values);
1393}
1394
1395/*
1396 * Function:
1397 *	SetChildrenPrefSizes
1398 *
1399 * Parameters:
1400 *	pw - paned widget
1401 *
1402 * Description:
1403 *	Sets the preferred sizes of the children.
1404 */
1405static void
1406SetChildrenPrefSizes(PanedWidget pw, unsigned int off_size)
1407{
1408    Widget *childP;
1409    Boolean vert = IsVert(pw);
1410    XtWidgetGeometry request, reply;
1411
1412    ForAllPanes(pw, childP)
1413	if (pw->paned.resize_children_to_pref || PaneInfo(*childP)->size == 0 ||
1414	    PaneInfo(*childP)->resize_to_pref) {
1415	    if (PaneInfo(*childP)->preferred_size != PANED_ASK_CHILD)
1416		PaneInfo(*childP)->wp_size = PaneInfo(*childP)->preferred_size;
1417	    else {
1418		if(vert) {
1419		    request.request_mode = CWWidth;
1420		    request.width = (Dimension) off_size;
1421		}
1422		else {
1423		    request.request_mode = CWHeight;
1424		    request.height = (Dimension) off_size;
1425		}
1426
1427		if ((XtQueryGeometry(*childP, &request, &reply)
1428		     == XtGeometryAlmost)
1429		    && (reply.request_mode = (vert ? CWHeight : CWWidth)))
1430		    PaneInfo(*childP)->wp_size = GetRequestInfo(&reply, vert);
1431		else
1432		    PaneInfo(*childP)->wp_size = PaneSize(*childP, vert);
1433	    }
1434
1435	    PaneInfo(*childP)->size = PaneInfo(*childP)->wp_size;
1436	}
1437}
1438
1439/*
1440 * Function:
1441 *	ChangeAllGripCursors
1442 *
1443 * Parameters:
1444 *	pw - paned widget
1445 *
1446 *	Description:
1447 *	Changes all the grip cursors.
1448 */
1449static void
1450ChangeAllGripCursors(PanedWidget pw)
1451{
1452    Widget *childP;
1453
1454    ForAllPanes(pw, childP) {
1455	Arg arglist[1];
1456	Cursor cursor;
1457
1458	if ((cursor = pw->paned.grip_cursor) == None) {
1459	    if (IsVert(pw))
1460		cursor = pw->paned.v_grip_cursor;
1461	    else
1462		cursor = pw->paned.h_grip_cursor;
1463	}
1464
1465	if (HasGrip(*childP)) {
1466	    XtSetArg(arglist[0], XtNcursor, cursor);
1467	    XtSetValues(PaneInfo(*childP)->grip, arglist, 1);
1468	}
1469    }
1470}
1471
1472/*
1473 * Function:
1474 *	PushPaneStack
1475 *
1476 * Parameters:
1477 *	pw   - paned widget
1478 *	pane - pane that we are pushing
1479 *
1480 * Description:
1481 *	Pushes a value onto the pane stack.
1482 */
1483static void
1484PushPaneStack(PanedWidget pw, Pane pane)
1485{
1486    PaneStack *stack = (PaneStack *)XtMalloc(sizeof(PaneStack));
1487
1488    stack->next = pw->paned.stack;
1489    stack->pane = pane;
1490    stack->start_size = pane->size;
1491
1492    pw->paned.stack = stack;
1493}
1494
1495/*
1496 * Function:
1497 *	GetPaneStack
1498 *
1499 * Parameters:
1500 *	pw - paned widget
1501 *	shrink	   - True if we want to shrink this pane, False otherwise
1502 *	pane	   - pane that we are popping (return)
1503 *	start_size - size that this pane started at (return)
1504 *
1505 * Description:
1506 *	Gets the top value from the pane stack.
1507 */
1508static void
1509GetPaneStack(PanedWidget pw, Bool shrink, Pane *pane, int *start_size)
1510{
1511    if (pw->paned.stack == NULL) {
1512	*pane = NULL;
1513	return;
1514    }
1515
1516    *pane = pw->paned.stack->pane;
1517    *start_size = pw->paned.stack->start_size;
1518
1519    if (shrink != ((*pane)->size > *start_size))
1520	*pane = NULL;
1521}
1522
1523/*
1524 * Function:
1525 *	PopPaneStack
1526 *
1527 * Parameters:
1528 *	pw - paned widget
1529 *
1530 * Description:
1531 *	Pops the top item off the pane stack.
1532 *
1533 * Returns: True if this is not the last element on the stack
1534 */
1535static Bool
1536PopPaneStack(PanedWidget pw)
1537{
1538    PaneStack *stack = pw->paned.stack;
1539
1540    if (stack == NULL)
1541	return (False);
1542
1543    pw->paned.stack = stack->next;
1544    XtFree((char *)stack);
1545
1546    if (pw->paned.stack == NULL)
1547	return (False);
1548
1549    return (True);
1550}
1551
1552/*
1553 * Function:
1554 *	ClearPaneStack
1555 *
1556 * Parameters:
1557 *	pw - paned widget
1558 *
1559 * Description:
1560 *	Removes all entries from the pane stack.
1561 */
1562static void
1563ClearPaneStack(PanedWidget pw)
1564{
1565    while(PopPaneStack(pw))
1566	;
1567}
1568
1569static void
1570XawPanedClassInitialize(void)
1571{
1572    XawInitializeWidgetSet();
1573    XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
1574		   NULL, 0);
1575    XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString,
1576		       NULL, 0, XtCacheNone, NULL);
1577}
1578
1579/* The Geometry Manager only allows changes after Realize if
1580 * allow_resize is True in the constraints record.
1581 *
1582 * For vertically paned widgets:
1583 *
1584 * It only allows height changes, but offers the requested height
1585 * as a compromise if both width and height changes were requested.
1586 *
1587 * For horizontal widgets the converse is true.
1588 * As all good Geometry Managers should, we will return No if the
1589 * request will have no effect; i.e. when the requestor is already
1590 * of the desired geometry.
1591 */
1592static XtGeometryResult
1593XawPanedGeometryManager(Widget w, XtWidgetGeometry *request,
1594			XtWidgetGeometry *reply)
1595{
1596    PanedWidget pw = (PanedWidget)XtParent(w);
1597    XtGeometryMask mask = request->request_mode;
1598    Dimension old_size, old_wpsize, old_paned_size;
1599    Pane pane = PaneInfo(w);
1600    Boolean vert = IsVert(pw);
1601    Dimension on_size, off_size;
1602    XtGeometryResult result;
1603    Boolean almost = False;
1604
1605    /*
1606     * If any of the following is true, disallow the geometry change
1607     *
1608     * o The paned widget is realized and allow_resize is false for the pane
1609     * o The child did not ask to change the on_size
1610     * o The request is not a width or height request
1611     * o The requested size is the same as the current size
1612     */
1613
1614    if ((XtIsRealized((Widget)pw) && !pane->allow_resize)
1615	|| !(mask & (vert ? CWHeight : CWWidth))
1616	|| (mask & (XtGeometryMask)(~(CWWidth | CWHeight)))
1617	|| GetRequestInfo(request, vert) ==  PaneSize(w, vert))
1618	return (XtGeometryNo);
1619
1620    old_paned_size = PaneSize((Widget)pw, vert);
1621    old_wpsize = pane->wp_size;
1622    old_size = (Dimension)pane->size;
1623
1624    pane->wp_size = (Dimension)(pane->size = GetRequestInfo(request, vert));
1625
1626    AdjustPanedSize(pw, PaneSize((Widget)pw, !vert), &result, &on_size,
1627		    &off_size);
1628
1629    /*
1630     * Fool the Refigure Locations proc to thinking that we are
1631     * a different on_size
1632     */
1633
1634    if (result != XtGeometryNo) {
1635	if (vert)
1636	    XtHeight(pw) = on_size;
1637	else
1638	    XtWidth(pw) = on_size;
1639    }
1640
1641    RefigureLocations(pw, PaneIndex(w), AnyPane);
1642
1643    /*
1644     * Set up reply struct and reset core on_size
1645     */
1646    if (vert) {
1647	XtHeight(pw) = old_paned_size;
1648	reply->height = (Dimension) pane->size;
1649	reply->width = off_size;
1650    }
1651    else {
1652	XtWidth(pw) = old_paned_size;
1653	reply->height = off_size;
1654	reply->width = (Dimension) pane->size;
1655    }
1656
1657    /*
1658     * IF either of the following is true
1659     *
1660     * o There was a "off_size" request and the new "off_size" is different
1661     *   from that requested
1662     * o There was no "off_size" request and the new "off_size" is different
1663     *
1664     * o The "on_size" we will allow is different from that requested
1665     *
1666     * THEN: set almost
1667     */
1668    if (!((vert ? CWWidth : CWHeight) & mask)) {
1669	if (vert)
1670	    request->width = XtWidth(w);
1671	else
1672	    request->height = XtHeight(w);
1673    }
1674
1675    almost = GetRequestInfo(request, !vert) != GetRequestInfo(reply, !vert);
1676    almost = (Boolean)(almost | (GetRequestInfo(request, vert) != GetRequestInfo(reply, vert)));
1677
1678    if ((mask & XtCWQueryOnly) || almost) {
1679	pane->wp_size = old_wpsize;
1680	pane->size = old_size;
1681	RefigureLocations(pw, PaneIndex(w), AnyPane);
1682	reply->request_mode = CWWidth | CWHeight;
1683	if (almost)
1684	    return (XtGeometryAlmost);
1685    }
1686    else {
1687	AdjustPanedSize(pw, PaneSize((Widget) pw, !vert), NULL, NULL, NULL);
1688	CommitNewLocations(pw);		/* layout already refigured */
1689    }
1690
1691    return (XtGeometryDone);
1692}
1693
1694/*ARGSUSED*/
1695static void
1696XawPanedInitialize(Widget request _X_UNUSED, Widget cnew,
1697		   ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
1698{
1699    PanedWidget pw = (PanedWidget)cnew;
1700
1701    GetGCs((Widget)pw);
1702
1703    pw->paned.recursively_called = False;
1704    pw->paned.stack = NULL;
1705    pw->paned.resize_children_to_pref = True;
1706    pw->paned.num_panes = 0;
1707}
1708
1709static void
1710XawPanedRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
1711{
1712    PanedWidget pw = (PanedWidget)w;
1713    Widget *childP;
1714
1715    if ((attributes->cursor = pw->paned.cursor) != None)
1716	*valueMask |= CWCursor;
1717
1718    (*SuperClass->core_class.realize)(w, valueMask, attributes);
1719
1720    /*
1721     * Before we commit the new locations we need to realize all the panes and
1722     * their grips
1723     */
1724    ForAllPanes(pw, childP) {
1725	XtRealizeWidget(*childP);
1726	if (HasGrip(*childP))
1727	    XtRealizeWidget(PaneInfo(*childP)->grip);
1728    }
1729
1730    RefigureLocationsAndCommit(w);
1731    pw->paned.resize_children_to_pref = False;
1732}
1733
1734static void
1735XawPanedDestroy(Widget w)
1736{
1737    ReleaseGCs(w);
1738}
1739
1740static void
1741ReleaseGCs(Widget w)
1742{
1743    PanedWidget pw = (PanedWidget)w;
1744
1745    XtReleaseGC(w, pw->paned.normgc);
1746    XtReleaseGC(w, pw->paned.invgc);
1747    XtReleaseGC(w, pw->paned.flipgc);
1748}
1749
1750static void
1751XawPanedInsertChild(Widget w)
1752{
1753    Pane pane = PaneInfo(w);
1754
1755    /* insert the child widget in the composite children list with the
1756       superclass insert_child routine
1757     */
1758    (*SuperClass->composite_class.insert_child)(w);
1759
1760    if (!IsPane(w))
1761	return;
1762
1763    if (pane->show_grip == True) {
1764	CreateGrip(w);
1765	if (pane->min == PANED_GRIP_SIZE)
1766	    pane->min = PaneSize(pane->grip, IsVert((PanedWidget)XtParent(w)));
1767    }
1768    else {
1769	if (pane->min == PANED_GRIP_SIZE)
1770	    pane->min = 1;
1771	pane->grip = NULL;
1772    }
1773
1774    pane->size = 0;
1775    pane->paned_adjusted_me = False;
1776}
1777
1778static void
1779XawPanedDeleteChild(Widget w)
1780{
1781    /* remove the subwidget info and destroy the grip */
1782    if (IsPane(w) && HasGrip(w))
1783	XtDestroyWidget(PaneInfo(w)->grip);
1784
1785    /* delete the child widget in the composite children list with the
1786       superclass delete_child routine
1787     */
1788    (*SuperClass->composite_class.delete_child)(w);
1789}
1790
1791static void
1792XawPanedChangeManaged(Widget w)
1793{
1794    PanedWidget pw = (PanedWidget)w;
1795    Boolean vert = IsVert(pw);
1796    Dimension size;
1797    Widget *childP;
1798
1799    if (pw->paned.recursively_called++)
1800	return;
1801
1802    /*
1803     * If the size is zero then set it to the size of the widest or tallest pane
1804     */
1805
1806    if ((size = PaneSize((Widget)pw, !vert)) == 0) {
1807	size = 1;
1808	ForAllChildren(pw, childP)
1809	if (XtIsManaged(*childP) && (PaneSize(*childP, !vert) > size))
1810	    size = PaneSize(*childP, !vert);
1811    }
1812
1813    ManageAndUnmanageGrips(pw);
1814    pw->paned.recursively_called = False;
1815    ResortChildren(pw);
1816
1817    pw->paned.num_panes = 0;
1818    ForAllChildren(pw, childP)
1819	if (IsPane(*childP)) {
1820	    if (XtIsManaged(*childP)) {
1821		Pane pane = PaneInfo(*childP);
1822
1823		if (HasGrip(*childP))
1824		    PaneInfo(pane->grip)->position = pw->paned.num_panes;
1825		pane->position = pw->paned.num_panes; /* TEMPORARY -CDP 3/89 */
1826		pw->paned.num_panes++;
1827	    }
1828	    else
1829		break;		 /* This list is already sorted */
1830	}
1831
1832    SetChildrenPrefSizes((PanedWidget) w, size);
1833
1834    /*
1835     * ForAllPanes can now be used
1836     */
1837    if (PaneSize((Widget) pw, vert) == 0)
1838	AdjustPanedSize(pw, size, NULL, NULL, NULL);
1839
1840    if (XtIsRealized((Widget)pw))
1841	RefigureLocationsAndCommit((Widget)pw);
1842}
1843
1844static void
1845XawPanedResize(Widget w)
1846{
1847    SetChildrenPrefSizes((PanedWidget)w,
1848			 PaneSize(w, !IsVert((PanedWidget)w)));
1849    RefigureLocationsAndCommit(w);
1850}
1851
1852/*ARGSUSED*/
1853static void
1854XawPanedRedisplay(Widget w, XEvent *event _X_UNUSED, Region region _X_UNUSED)
1855{
1856    DrawInternalBorders((PanedWidget)w);
1857}
1858
1859/*ARGSUSED*/
1860static Boolean
1861XawPanedSetValues(Widget old, Widget request _X_UNUSED, Widget cnew,
1862		  ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
1863{
1864    PanedWidget old_pw = (PanedWidget)old;
1865    PanedWidget new_pw = (PanedWidget)cnew;
1866    Boolean redisplay = False;
1867
1868    if ((old_pw->paned.cursor != new_pw->paned.cursor) && XtIsRealized(cnew))
1869	XDefineCursor(XtDisplay(cnew), XtWindow(cnew), new_pw->paned.cursor);
1870
1871    if (old_pw->paned.internal_bp != new_pw->paned.internal_bp ||
1872	old_pw->core.background_pixel != new_pw->core.background_pixel) {
1873	ReleaseGCs(old);
1874	GetGCs(cnew);
1875	redisplay = True;
1876    }
1877
1878    if (old_pw->paned.grip_cursor != new_pw->paned.grip_cursor ||
1879	old_pw->paned.v_grip_cursor != new_pw->paned.v_grip_cursor ||
1880	old_pw->paned.h_grip_cursor != new_pw->paned.h_grip_cursor)
1881	ChangeAllGripCursors(new_pw);
1882
1883    if (IsVert(old_pw) != IsVert(new_pw)) {
1884	/*
1885	 * We are fooling the paned widget into thinking that is needs to
1886	 * fully refigure everything, which is what we want
1887	 */
1888	if (IsVert(new_pw))
1889	    XtWidth(new_pw) = 0;
1890	else
1891	    XtHeight(new_pw) = 0;
1892
1893	new_pw->paned.resize_children_to_pref = True;
1894	XawPanedChangeManaged(cnew); /* Seems weird, but does the right thing */
1895	new_pw->paned.resize_children_to_pref = False;
1896	if (new_pw->paned.grip_cursor == None)
1897	    ChangeAllGripCursors(new_pw);
1898	return (True);
1899    }
1900
1901    if (old_pw->paned.internal_bw != new_pw->paned.internal_bw) {
1902	AdjustPanedSize(new_pw, PaneSize(cnew, !IsVert(old_pw)),
1903		        NULL, NULL, NULL);
1904	RefigureLocationsAndCommit(cnew);
1905	return (True);		/* We have done a full configuration, return */
1906    }
1907
1908    if (old_pw->paned.grip_indent != new_pw->paned.grip_indent &&
1909	XtIsRealized(cnew)) {
1910	CommitNewLocations(new_pw);
1911	redisplay = True;
1912    }
1913
1914    return (redisplay);
1915}
1916
1917/*ARGSUSED*/
1918static Boolean
1919XawPanedPaneSetValues(Widget old, Widget request _X_UNUSED, Widget cnew,
1920		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
1921{
1922    Pane old_pane = PaneInfo(old);
1923    Pane new_pane = PaneInfo(cnew);
1924    Boolean redisplay = False;
1925
1926    /* Check for new min and max */
1927    if (old_pane->min != new_pane->min || old_pane->max != new_pane->max)
1928	XawPanedSetMinMax(cnew, (int)new_pane->min, (int)new_pane->max);
1929
1930    /* Check for change in XtNshowGrip */
1931    if (old_pane->show_grip != new_pane->show_grip) {
1932	if (new_pane->show_grip == True) {
1933	    CreateGrip(cnew);
1934	    if (XtIsRealized(XtParent(cnew))) {
1935		if (XtIsManaged(cnew))	/* if paned is unrealized this will
1936					   happen automatically at realize time
1937					 */
1938		    XtManageChild(PaneInfo(cnew)->grip);	/* manage the grip */
1939		XtRealizeWidget(PaneInfo(cnew)->grip); /* realize the grip */
1940		CommitNewLocations((PanedWidget)XtParent(cnew));
1941	    }
1942	}
1943	else if (HasGrip(old)) {
1944	    XtDestroyWidget(old_pane->grip);
1945	    new_pane->grip = NULL;
1946	    redisplay = True;
1947	}
1948    }
1949
1950    return (redisplay);
1951}
1952
1953/*
1954 * Public routines
1955 */
1956/*
1957 * Function:
1958 *	XawPanedSetMinMax
1959 *
1960 * Parameters:
1961 *	widget - widget that is a child of the Paned widget
1962 *	min    - new min and max size for the pane
1963 *	max    - ""
1964 *
1965 * Description:
1966 *	Sets the min and max size for a pane.
1967 */
1968void
1969XawPanedSetMinMax(Widget widget, int min, int max)
1970{
1971    Pane pane = PaneInfo(widget);
1972
1973    pane->min = (Dimension) min;
1974    pane->max = (Dimension) max;
1975    RefigureLocationsAndCommit(widget->core.parent);
1976}
1977
1978/*
1979 * Function:
1980 *	XawPanedGetMinMax
1981 *
1982 * Parameters:
1983 *	widget - widget that is a child of the Paned widget
1984 *	min    - current min and max size for the pane (return)
1985 *	max    - ""
1986 *
1987 * Description:
1988 *	Gets the min and max size for a pane.
1989 */
1990void
1991XawPanedGetMinMax(Widget widget, int *min, int *max)
1992{
1993    Pane pane = PaneInfo(widget);
1994
1995    *min = pane->min;
1996    *max = pane->max;
1997}
1998
1999/*
2000 * Function:
2001 *	XawPanedSetRefigureMode
2002 *
2003 * Parameters:
2004 *	w    - paned widget
2005 *	mode - if False then inhibit refigure
2006 *
2007 * Description:
2008 *	Allows a flag to be set the will inhibit
2009 *		   the paned widgets relayout routine.
2010 */
2011void
2012XawPanedSetRefigureMode(Widget w,
2013#if NeedWidePrototypes
2014	int mode
2015#else
2016	Boolean mode
2017#endif
2018)
2019{
2020    ((PanedWidget)w)->paned.refiguremode = mode;
2021    RefigureLocationsAndCommit(w);
2022}
2023
2024/*
2025 * Function:
2026 *	XawPanedGetNumSub
2027 *
2028 * Parameters:
2029 *	w - paned widget
2030 *
2031 * Description:
2032 *	Returns the number of panes in the paned widget.
2033 * Returns:
2034 *	the number of panes in the paned widget
2035 */
2036int
2037XawPanedGetNumSub(Widget w)
2038{
2039    return (((PanedWidget)w)->paned.num_panes);
2040}
2041
2042/*
2043 * Function:
2044 *	XawPanedAllowResize
2045 *
2046 * Parameters:
2047 *	widget - child of the paned widget
2048 *
2049 * Description:
2050 *	  Allows a flag to be set that determines if the paned
2051 *	widget will allow geometry requests from this child.
2052 */
2053void
2054XawPanedAllowResize(Widget widget,
2055#if NeedWidePrototypes
2056	int allow_resize
2057#else
2058	Boolean allow_resize
2059#endif
2060)
2061{
2062    PaneInfo(widget)->allow_resize = allow_resize;
2063}
2064