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