Build Fix -- compatibility issue with newer autoconf
[sxemacs] / src / ui / lwlib / xlwtabs.c
1  /* Tabs Widget for SXEmacs.
2     Copyright (C) 1999 Edward A. Falk
3
4     This file is part of SXEmacs.
5
6 SXEmacs is free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation, either version 3 of the License, or (at your
9 option) any later version.
10
11 SXEmacs is distributed in the hope that it will be
12 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19  /* Synched up with: Tabs.c 1.27.
20
21     #### This file contains essential XEmacs related fixes to the original
22     verison of the Tabs widget. Be VERY careful about syncing if you ever
23     update to a more recent version. In general this is probably now a
24     bad idea. */
25
26  /*
27   * Tabs.c - Index Tabs composite widget
28   *
29   * Author: Edward A. Falk
30   *        falk@falconer.vip.best.com
31   *
32   * Date: July 29, 1997
33   *
34   *
35   * Overall layout of this widget is as follows:
36   *
37   *   ________ ,---------. _________
38   *  |  label ||  Label  ||  Label  |  \ tabs
39   *  |________||         ||_________|  /
40   *  |+----------------------------+|  \
41   *  ||                            ||  |
42   *  ||  child widget window       ||   > frame
43   *  |+----------------------------+|  |
44   *  +------------------------------+  /
45   *
46   * The height of the tabs includes the shadow width, top and bottom
47   * margins, and the height of the text.
48   *
49   * The height of the frame includes the top and bottom shadow width and the
50   * size of the child widget window.
51   *
52   * The tabs overlap the frame and each other vertically by the shadow
53   * width, so that when the topmost tab is drawn, it obliterates part of
54   * the frame.
55   */
56
57 /*
58  * TODO: min child height = tab height
59  */
60
61 #include        <config.h>
62 #include        <stdio.h>
63
64 #include        <X11/Xlib.h>
65 #include        <X11/IntrinsicP.h>
66 #include        <X11/StringDefs.h>
67
68 #include        "lwlib-internal.h"
69 #include        "ui/X11/xmu.h"
70 #include        "xlwtabsP.h"
71 #include        "xlwgcs.h"
72
73 #define MIN_WID         10
74 #define MIN_HGT         10
75 #define INDENT          3       /* tabs indented from edge by this much */
76 #define SPACING         0       /* distance between tabs */
77 #define SHADWID         1       /* default shadow width */
78 #define TABDELTA        2       /* top tab grows this many pixels */
79 #define TABLDELTA       2       /* top tab label offset this many pixels */
80
81 /****************************************************************
82  *
83  * IndexTabs Resources
84  *
85  ****************************************************************/
86
87 static char defaultTranslations[] = "\
88         <BtnUp>:                select()        \n\
89         <FocusIn>:              highlight()     \n\
90         <FocusOut>:             unhighlight()   \n\
91         <Key>Page_Up:           page(up)        \n\
92         <Key>KP_Page_Up:        page(up)        \n\
93         <Key>Prior:             page(up)        \n\
94         <Key>KP_Prior:          page(up)        \n\
95         <Key>Page_Down:         page(down)      \n\
96         <Key>KP_Page_Down:      page(down)      \n\
97         <Key>Next:              page(down)      \n\
98         <Key>KP_Next:           page(down)      \n\
99         <Key>Home:              page(home)      \n\
100         <Key>KP_Home:           page(home)      \n\
101         <Key>End:               page(end)       \n\
102         <Key>KP_End:            page(end)       \n\
103         <Key>Up:                highlight(up)   \n\
104         <Key>KP_Up:             highlight(up)   \n\
105         <Key>Down:              highlight(down) \n\
106         <Key>KP_Down:           highlight(down) \n\
107         <Key> :                 page(select)    \n\
108          ";
109
110 static char accelTable[] = "    #augment\n\
111         <Key>Page_Up:           page(up)        \n\
112         <Key>KP_Page_Up:        page(up)        \n\
113         <Key>Prior:             page(up)        \n\
114         <Key>KP_Prior:          page(up)        \n\
115         <Key>Page_Down:         page(down)      \n\
116         <Key>KP_Page_Down:      page(down)      \n\
117         <Key>Next:              page(down)      \n\
118         <Key>KP_Next:           page(down)      \n\
119         <Key>Home:              page(home)      \n\
120         <Key>KP_Home:           page(home)      \n\
121         <Key>End:               page(end)       \n\
122         <Key>KP_End:            page(end)       \n\
123         <Key>Up:                highlight(up)   \n\
124         <Key>KP_Up:             highlight(up)   \n\
125         <Key>Down:              highlight(down) \n\
126         <Key>KP_Down:           highlight(down) \n\
127         <Key> :                 page(select)    \n\
128          ";
129 static XtAccelerators defaultAccelerators;      /* #### Never used */
130
131 #define offset(field)   XtOffsetOf(TabsRec, tabs.field)
132 static XtResource resources[] = {
133
134         {XtNselectInsensitive, XtCSelectInsensitive, XtRBoolean,
135          sizeof(Boolean),
136          offset(selectInsensitive), XtRImmediate, (XtPointer) True}
137         ,
138         {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
139          offset(font), XtRString, (XtPointer) XtDefaultFont}
140         ,
141         {XtNinternalWidth, XtCWidth, XtRDimension, sizeof(Dimension),
142          offset(internalWidth), XtRImmediate, (XtPointer) 4}
143         ,
144         {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension),
145          offset(internalHeight), XtRImmediate, (XtPointer) 4}
146         ,
147         {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
148          XtOffsetOf(RectObjRec, rectangle.border_width), XtRImmediate,
149          (XtPointer) 0}
150         ,
151         {XtNtopWidget, XtCTopWidget, XtRWidget, sizeof(Widget),
152          offset(topWidget), XtRImmediate, NULL}
153         ,
154         {XtNhighlightWidget, XtCHighlightWidget, XtRWidget, sizeof(Widget),
155          offset(hilight), XtRImmediate, NULL}
156         ,
157         {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
158          offset(callbacks), XtRCallback, NULL}
159         ,
160         {XtNpopdownCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
161          offset(popdownCallbacks), XtRCallback, NULL}
162         ,
163         {XtNbeNiceToColormap, XtCBeNiceToColormap, XtRBoolean, sizeof(Boolean),
164          offset(be_nice_to_cmap), XtRImmediate, (XtPointer) True}
165         ,
166         {XtNtopShadowContrast, XtCTopShadowContrast, XtRInt, sizeof(int),
167          offset(top_shadow_contrast), XtRImmediate, (XtPointer) 20},
168         {XtNbottomShadowContrast, XtCBottomShadowContrast, XtRInt, sizeof(int),
169          offset(bot_shadow_contrast), XtRImmediate, (XtPointer) 40},
170         {XtNinsensitiveContrast, XtCInsensitiveContrast, XtRInt, sizeof(int),
171          offset(insensitive_contrast), XtRImmediate, (XtPointer) 33},
172         {XtNaccelerators, XtCAccelerators, XtRAcceleratorTable,
173          sizeof(XtTranslations),
174          XtOffsetOf(TabsRec, core.accelerators), XtRString, accelTable}
175         ,
176 };
177 #undef  offset
178
179         /* constraint resources */
180
181 #define offset(field)   XtOffsetOf(TabsConstraintsRec, tabs.field)
182 static XtResource tabsConstraintResources[] = {
183         {XtNtabLabel, XtCLabel, XtRString, sizeof(String),
184          offset(label), XtRString, NULL}
185         ,
186         {XtNtabLeftBitmap, XtCLeftBitmap, XtRBitmap, sizeof(Pixmap),
187          offset(left_bitmap), XtRImmediate, None}
188         ,
189         {XtNtabForeground, XtCForeground, XtRPixel, sizeof(Pixel),
190          offset(foreground), XtRString, (XtPointer) XtDefaultForeground}
191         ,
192         {XtNresizable, XtCResizable, XtRBoolean, sizeof(Boolean),
193          offset(resizable), XtRImmediate, (XtPointer) True}
194         ,
195 };
196 #undef  offset
197
198 #if     !NeedFunctionPrototypes
199
200         /* FORWARD REFERENCES: */
201
202         /* member functions */
203
204 static void TabsClassInit();
205 static void TabsInit();
206 static void TabsResize();
207 static void TabsExpose();
208 static void TabsDestroy();
209 static void TabsRealize();
210 static Boolean TabsSetValues();
211 static Boolean TabsAcceptFocus();
212 static XtGeometryResult TabsQueryGeometry();
213 static XtGeometryResult TabsGeometryManager();
214 static void TabsChangeManaged();
215 static void TabsConstraintInitialize();
216 static Boolean TabsConstraintSetValues();
217
218         /* action procs */
219
220 static void TabsSelect();
221 static void TabsPage();
222 static void TabsHighlight();
223 static void TabsUnhighlight();
224
225         /* internal privates */
226
227 static void TabsAllocGCs();     /* get rendering GCs */
228 static void TabsFreeGCs();      /* return rendering GCs */
229 static void DrawTabs();         /* draw all tabs */
230 static void DrawTab();          /* draw one index tab */
231 static void DrawFrame();        /* draw frame around contents */
232 static void DrawTrim();         /* draw trim around a tab */
233 static void DrawBorder();       /* draw border */
234 static void DrawHighlight();    /* draw highlight */
235 static void UndrawTab();        /* undraw interior of a tab */
236 static void TabWidth();         /* recompute tab size */
237 static void GetPreferredSizes();        /* query all children for their sizes */
238 static void MaxChild();         /* find max preferred child size */
239 static int PreferredSize();     /* compute preferred size */
240 static int PreferredSize2();    /* compute preferred size */
241 static int PreferredSize3();    /* compute preferred size */
242 static void MakeSizeRequest();  /* try to change size */
243 static void getBitmapInfo();
244 static int TabLayout();         /* lay out tabs */
245 static void TabsShuffleRows();  /* bring current tab to bottom row */
246
247 static void TabsAllocFgGC();
248 static void TabsAllocGreyGC();
249
250 #else
251
252 static void TabsClassInit(void);
253 static void TabsInit(Widget req, Widget new, ArgList, Cardinal * nargs);
254 static void TabsConstraintInitialize(Widget, Widget, ArgList, Cardinal *);
255 static void TabsRealize(Widget, Mask *, XSetWindowAttributes *);
256 static void TabsDestroy(Widget w);
257 static void TabsResize(Widget w);
258 static void TabsExpose(Widget w, XEvent * event, Region region);
259 static Boolean TabsSetValues(Widget, Widget, Widget, ArgList, Cardinal *);
260 static Boolean TabsAcceptFocus(Widget, Time *);
261 static Boolean TabsConstraintSetValues(Widget, Widget, Widget,
262                                        ArgList, Cardinal *);
263 static XtGeometryResult TabsQueryGeometry(Widget,
264                                           XtWidgetGeometry *,
265                                           XtWidgetGeometry *);
266 static XtGeometryResult TabsGeometryManager(Widget, XtWidgetGeometry *,
267                                             XtWidgetGeometry *);
268 static void TabsChangeManaged(Widget w);
269
270 static void TabsSelect(Widget, XEvent *, String *, Cardinal *);
271 static void TabsPage(Widget, XEvent *, String *, Cardinal *);
272 static void TabsHighlight(Widget, XEvent *, String *, Cardinal *);
273 static void TabsUnhighlight(Widget, XEvent *, String *, Cardinal *);
274
275 static void DrawTabs(TabsWidget tw, Bool labels);
276 static void DrawTab(TabsWidget tw, Widget child, Bool labels);
277 static void DrawFrame(TabsWidget tw);
278 static void DrawTrim(TabsWidget, int x, int y,
279                      int wid, int hgt, Bool bottom, Bool undraw);
280 static void DrawBorder(TabsWidget tw, Widget child, Bool undraw);
281 static void DrawHighlight(TabsWidget tw, Widget child, Bool undraw);
282 static void UndrawTab(TabsWidget tw, Widget child);
283
284 static void TabWidth(Widget w);
285 static int TabLayout(TabsWidget, Dimension wid, Dimension hgt,
286                      Dimension * r_hgt, Bool query_only);
287 static void GetPreferredSizes(TabsWidget);
288 static void MaxChild(TabsWidget, Widget except, Dimension, Dimension);
289 static void TabsShuffleRows(TabsWidget tw);
290 static int PreferredSize(TabsWidget,
291                          Dimension * reply_width, Dimension * reply_height,
292                          Dimension * reply_cw, Dimension * reply_ch);
293 static int PreferredSize2(TabsWidget, Dimension cw, Dimension ch,
294                           Dimension * rw, Dimension * rh);
295 static int PreferredSize3(TabsWidget, Dimension wid, Dimension hgt,
296                           Dimension * rw, Dimension * rh);
297 static void MakeSizeRequest(TabsWidget);
298
299 static void TabsAllocGCs(TabsWidget);
300 static void TabsFreeGCs(TabsWidget);
301 static void getBitmapInfo(TabsWidget tw, TabsConstraints tab);
302 static void TabsAllocFgGC(TabsWidget tw);
303 static void TabsAllocGreyGC(TabsWidget tw);
304
305 #endif
306
307 #define AddRect(i,xx,yy,w,h)    \
308   do{rects[(i)].x=(xx); rects[i].y=(yy);        \
309      rects[i].width=(w); rects[i].height=(h);}while(0)
310
311 static XtActionsRec actionsList[] = {
312         {"select", TabsSelect},
313         {"page", TabsPage},
314         {"highlight", TabsHighlight},
315         {"unhighlight", TabsUnhighlight},
316 };
317
318 /****************************************************************
319 *
320 * Full class record constant
321 *
322 ****************************************************************/
323
324 #ifndef NEED_MOTIF
325 #define SuperClass      (&constraintClassRec)
326 #else
327 #define SuperClass      (&xmManagerClassRec)
328 #endif
329
330 TabsClassRec tabsClassRec = {
331         {
332 /* core_class fields      */
333          /* superclass         */ (WidgetClass) SuperClass,
334          /* class_name         */ "Tabs",
335          /* widget_size        */ sizeof(TabsRec),
336          /* class_initialize   */ TabsClassInit,
337                                         /* class_part_init    */ NULL,
338                                         /* TODO? */
339          /* class_inited       */ FALSE,
340          /* initialize         */ TabsInit,
341          /* initialize_hook    */ NULL,
342          /* realize            */ TabsRealize,
343          /* actions            */ actionsList,
344          /* num_actions        */ XtNumber(actionsList),
345          /* resources          */ resources,
346          /* num_resources      */ XtNumber(resources),
347          /* xrm_class          */ NULLQUARK,
348          /* compress_motion    */ TRUE,
349 #if XtSpecificationRelease < 6
350          /* compress_exposure  */ XtExposeCompressMaximal,
351 #else
352          /* compress_exposure  */ XtExposeCompressMaximal | XtExposeNoRegion,
353 #endif
354          /* compress_enterleave */ TRUE,
355          /* visible_interest   */ TRUE,
356          /* destroy            */ TabsDestroy,
357          /* resize             */ TabsResize,
358          /* expose             */ TabsExpose,
359          /* set_values         */ TabsSetValues,
360          /* set_values_hook    */ NULL,
361          /* set_values_almost  */ XtInheritSetValuesAlmost,
362          /* get_values_hook    */ NULL,
363          /* accept_focus       */ TabsAcceptFocus,
364          /* version            */ XtVersion,
365          /* callback_private   */ NULL,
366          /* tm_table           */ defaultTranslations,
367          /* query_geometry     */ TabsQueryGeometry,
368          /* display_accelerator */ XtInheritDisplayAccelerator,
369          /* extension          */ NULL
370          }
371         ,
372         {
373 /* composite_class fields */
374          /* geometry_manager   */ TabsGeometryManager,
375          /* change_managed     */ TabsChangeManaged,
376                                                         /* insert_child       */ XtInheritInsertChild,
377                                                         /* TODO? */
378                                                         /* delete_child       */ XtInheritDeleteChild,
379                                                         /* TODO? */
380          /* extension          */ NULL
381          }
382         ,
383         {
384 /* constraint_class fields */
385          /* subresources       */ tabsConstraintResources,
386          /* subresource_count  */ XtNumber(tabsConstraintResources),
387          /* constraint_size    */ sizeof(TabsConstraintsRec),
388          /* initialize         */ TabsConstraintInitialize,
389          /* destroy            */ NULL,
390          /* set_values         */ TabsConstraintSetValues,
391          /* extension          */ NULL,
392          }
393         ,
394 #ifdef  NEED_MOTIF
395 /* Manager Class fields */
396         {
397          /* translations             */ NULL,
398          /* syn_resources            */ NULL,
399          /* num_syn_resources        */ 0,
400          /* syn_constraint_resources */ NULL,
401          /* num_syn_constraint_resources */ 0,
402          /* parent_process           */ XmInheritParentProcess,
403          /* extension                */ NULL
404          }
405         ,
406 #endif
407         {
408 /* Tabs class fields */
409          /* extension          */ NULL,
410          }
411 };
412
413 WidgetClass tabsWidgetClass = (WidgetClass) & tabsClassRec;
414
415 #define TabsNumChildren(tw) (((TabsWidget)tw)->composite.num_children)
416 #define TabVisible(tab) \
417         (XtIsManaged(tab) && \
418          ((TabsConstraints)((tab)->core.constraints))->tabs.visible)
419 \f
420 /****************************************************************
421  *
422  * Member Procedures
423  *
424  ****************************************************************/
425
426 static void TabsClassInit(void)
427 {
428         defaultAccelerators = XtParseAcceleratorTable(accelTable);
429         /* TODO: register converter for labels? */
430 }
431
432         /* Init a newly created tabs widget.  Compute height of tabs
433          * and optionally compute size of widget. */
434
435 /* ARGSUSED */
436
437 static void
438 TabsInit(Widget request, Widget new, ArgList args, Cardinal * num_args)
439 {
440         TabsWidget newTw = (TabsWidget) new;
441
442         newTw->tabs.numRows = 0;
443         newTw->tabs.realRows = 0;
444
445         GetPreferredSizes(newTw);
446
447         /* height is easy, it's the same for all tabs:
448          *  TODO: font height + height of tallest bitmap.
449          */
450         newTw->tabs.tab_height = 2 * newTw->tabs.internalHeight + SHADWID;
451
452         if (newTw->tabs.font != NULL)
453                 newTw->tabs.tab_height += newTw->tabs.font->max_bounds.ascent +
454                     newTw->tabs.font->max_bounds.descent;
455
456         /* GC allocation is deferred until XtRealize() */
457
458         /* if size not explicitly set, set it to our preferred size now. */
459
460         if (request->core.width == 0 || request->core.height == 0) {
461                 Dimension w, h;
462                 PreferredSize(newTw, &w, &h, NULL, NULL);
463                 if (request->core.width == 0)
464                         new->core.width = w;
465                 if (request->core.height == 0)
466                         new->core.height = h;
467                 XtClass(new)->core_class.resize(new);
468         }
469
470         /* defer GC allocation, etc., until Realize() time. */
471         newTw->tabs.foregroundGC =
472             newTw->tabs.backgroundGC =
473             newTw->tabs.greyGC = newTw->tabs.topGC = newTw->tabs.botGC = None;
474
475         newTw->tabs.grey50 = None;
476
477         newTw->tabs.needs_layout = False;
478
479         newTw->tabs.hilight = NULL;
480
481 #ifdef  NEED_MOTIF
482         newTw->manager.navigation_type = XmTAB_GROUP;
483         newTw->manager.traversal_on = True;
484 #endif
485 }
486
487         /* Init the constraint part of a new tab child.  Compute the
488          * size of the tab.
489          */
490 /* ARGSUSED */
491 static void
492 TabsConstraintInitialize(Widget request, Widget new,
493                          ArgList args, Cardinal * num_args)
494 {
495         TabsConstraints tab = (TabsConstraints) new->core.constraints;
496         tab->tabs.greyAlloc = False;    /* defer allocation of pixel */
497         tab->tabs.visible = False;
498
499         getBitmapInfo((TabsWidget) XtParent(new), tab);
500         TabWidth(new);
501 }
502
503         /* Called when tabs widget first realized.  Create the window
504          * and allocate the GCs
505          */
506
507 static void
508 TabsRealize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes)
509 {
510         TabsWidget tw = (TabsWidget) w;
511
512         attributes->bit_gravity = NorthWestGravity;
513         *valueMask |= CWBitGravity;
514
515         SuperClass->core_class.realize(w, valueMask, attributes);
516
517         TabsAllocGCs(tw);
518 }
519
520 static void TabsDestroy(Widget w)
521 {
522         TabsFreeGCs((TabsWidget) w);
523 }
524
525         /* Parent has resized us.  This will require that the tabs be
526          * laid out again.
527          */
528
529 static void TabsResize(Widget w)
530 {
531         TabsWidget tw = (TabsWidget) w;
532         int i;
533         int num_children = tw->composite.num_children;
534         Widget *childP;
535         TabsConstraints tab;    /* #### unused */
536         Dimension cw, ch, bw;
537
538         /* Our size has now been dictated by the parent.  Lay out the
539          * tabs, lay out the frame, lay out the children.  Remember
540          * that the tabs overlap each other and the frame by shadowWidth.
541          * Also, the top tab is larger than the others, so if there's only
542          * one row, the widget must be made taller to accommodate this.
543          *
544          * Once the tabs are laid out, if there is more than one
545          * row, we may need to shuffle the rows to bring the top tab
546          * to the bottom row.
547          */
548
549         tw->tabs.needs_layout = False;
550
551         if (num_children > 0 && tw->composite.children != NULL) {
552                 /* Loop through the tabs and assign rows & x positions */
553                 (void)TabLayout(tw, tw->core.width, tw->core.height, NULL,
554                                 False);
555                 num_children = TabsNumChildren(tw);
556
557                 /* assign a top widget, bring it to bottom row. */
558                 TabsShuffleRows(tw);
559
560                 /* now assign child positions & sizes.  Positions are all the
561                  * same: just inside the frame.  Sizes are also all the same.
562                  */
563
564                 tw->tabs.child_width = cw = tw->core.width - 2 * SHADWID;
565                 tw->tabs.child_height = ch =
566                     tw->core.height < (tw->tabs.tab_total + 2 * SHADWID) ? 0 :
567                     tw->core.height - tw->tabs.tab_total - 2 * SHADWID;
568
569                 for (i = 0, childP = tw->composite.children;
570                      i < num_children; ++i, ++childP)
571                         if (XtIsManaged(*childP)) {
572                                 tab =
573                                     (TabsConstraints) (*childP)->core.
574                                     constraints;
575                                 SXE_SET_UNUSED(tab);
576                                 bw = (*childP)->core.border_width;
577                                 /* Don't do anything if we can't see any of the child. */
578                                 if (ch >= bw * 2 && ch > 0 && cw >= bw * 2
579                                     && cw > 0)
580                                         XtConfigureWidget(*childP, SHADWID,
581                                                           tw->tabs.tab_total +
582                                                           SHADWID, cw - bw * 2,
583                                                           ch - bw * 2, bw);
584                         }
585                 if (XtIsRealized(w)) {
586                         XClearWindow(XtDisplay((Widget) tw),
587                                      XtWindow((Widget) tw));
588                         /* should not be necessary to explicitly repaint after a
589                          * resize, but XEmacs folks tell me it is.
590                          */
591                         XtClass(tw)->core_class.expose((Widget) tw, NULL, None);
592                 }
593         }
594 }                               /* Resize */
595
596         /* Redraw entire Tabs widget */
597
598 /* ARGSUSED */
599 static void TabsExpose(Widget w, XEvent * event, Region region)
600 {
601         TabsWidget tw = (TabsWidget) w;
602
603         if (tw->tabs.needs_layout)
604                 XtClass(w)->core_class.resize(w);
605
606         DrawTabs(tw, True);
607 }
608
609         /* Called when any Tabs widget resources are changed. */
610
611 /* ARGSUSED */
612 static Boolean
613 TabsSetValues(Widget current, Widget request, Widget new,
614               ArgList args, Cardinal * num_args)
615 {
616         TabsWidget curtw = (TabsWidget) current;
617         TabsWidget tw = (TabsWidget) new;
618         Boolean needRedraw = False;
619         Widget *childP;
620         unsigned int i;
621
622         if (tw->tabs.font != curtw->tabs.font ||
623             tw->tabs.internalWidth != curtw->tabs.internalWidth ||
624             tw->tabs.internalHeight != curtw->tabs.internalHeight) {
625                 tw->tabs.tab_height = 2 * tw->tabs.internalHeight + SHADWID;
626
627                 if (tw->tabs.font != NULL)
628                         tw->tabs.tab_height +=
629                             tw->tabs.font->max_bounds.ascent +
630                             tw->tabs.font->max_bounds.descent;
631
632                 /* Tab size has changed.  Resize all tabs and request a new size */
633                 for (i = 0, childP = tw->composite.children;
634                      i < tw->composite.num_children; ++i, ++childP)
635                         if (XtIsManaged(*childP))
636                                 TabWidth(*childP);
637                 PreferredSize(tw, &tw->core.width, &tw->core.height, NULL,
638                               NULL);
639                 needRedraw = True;
640                 tw->tabs.needs_layout = True;
641         }
642
643         /* TODO: if any color changes, need to recompute GCs and redraw */
644
645         if (tw->core.background_pixel != curtw->core.background_pixel ||
646             tw->core.background_pixmap != curtw->core.background_pixmap ||
647             tw->tabs.font != curtw->tabs.font)
648                 if (XtIsRealized(new)) {
649                         TabsFreeGCs(tw);
650                         TabsAllocGCs(tw);
651                         needRedraw = True;
652                 }
653
654         if (tw->core.sensitive != curtw->core.sensitive)
655                 needRedraw = True;
656
657         /* Highlit widget changed */
658         if (tw->tabs.hilight != curtw->tabs.hilight) {
659                 needRedraw = True;
660         }
661
662         /* If top widget changes, need to change stacking order, redraw tabs.
663          * Window system will handle the redraws.
664          */
665
666         if (tw->tabs.topWidget != curtw->tabs.topWidget) {
667                 if (XtIsRealized(tw->tabs.topWidget)) {
668                         Widget w = tw->tabs.topWidget;
669                         TabsConstraints tab =
670                             (TabsConstraints) w->core.constraints;
671
672                         XRaiseWindow(XtDisplay(w), XtWindow(w));
673 #ifdef  NEED_MOTIF
674                         XtVaSetValues(curtw->tabs.topWidget, XmNtraversalOn,
675                                       False, 0);
676                         XtVaSetValues(w, XmNtraversalOn, True, 0);
677 #endif
678
679                         if ((int)tab->tabs.row != (int)tw->tabs.numRows - 1) {
680                                 TabsShuffleRows(tw);
681                         }
682                         needRedraw = True;
683                 } else
684                         tw->tabs.needs_layout = True;
685         }
686
687         return needRedraw;
688 }
689
690         /* Called when any child constraint resources change. */
691
692 /* ARGSUSED */
693 static Boolean
694 TabsConstraintSetValues(Widget current, Widget request, Widget new,
695                         ArgList args, Cardinal * num_args)
696 {
697         TabsWidget tw = (TabsWidget) XtParent(new);
698         TabsConstraints ctab = (TabsConstraints) current->core.constraints;
699         TabsConstraints tab = (TabsConstraints) new->core.constraints;
700
701         /* if label changes, need to re-layout the entire widget */
702         /* if foreground changes, need to redraw tab label */
703
704         /* TODO: only need resize of new bitmap has different dimensions
705          * from old bitmap.
706          */
707
708         if (tab->tabs.label != ctab->tabs.label ||      /* Tab size has changed. */
709             tab->tabs.left_bitmap != ctab->tabs.left_bitmap) {
710                 TabWidth(new);
711                 tw->tabs.needs_layout = True;
712
713                 if (tab->tabs.left_bitmap != ctab->tabs.left_bitmap)
714                         getBitmapInfo(tw, tab);
715
716                 /* If there are no subclass ConstraintSetValues procedures remaining
717                  * to be invoked, and if the preferred size has changed, ask
718                  * for a resize.
719                  */
720                 if (XtClass((Widget) tw) == tabsWidgetClass)
721                         MakeSizeRequest(tw);
722         }
723
724         /* The child widget itself never needs a redisplay, but the parent
725          * Tabs widget might.
726          */
727
728         if (XtIsRealized(new)) {
729                 if (tw->tabs.needs_layout) {
730                         XClearWindow(XtDisplay((Widget) tw),
731                                      XtWindow((Widget) tw));
732                         XtClass(tw)->core_class.expose((Widget) tw, NULL, None);
733                 }
734
735                 else if (tab->tabs.foreground != ctab->tabs.foreground)
736                         DrawTab(tw, new, True);
737         }
738
739         return False;
740 }
741
742 static Boolean TabsAcceptFocus(Widget w, Time * t)
743 {
744         if (!w->core.being_destroyed && XtIsRealized(w) &&
745             XtIsSensitive(w) && XtIsManaged(w) && w->core.visible) {
746                 Widget p;
747                 for (p = XtParent(w); !XtIsShell(p); p = XtParent(p)) ;
748                 XtSetKeyboardFocus(p, w);
749                 return True;
750         } else
751                 return False;
752 }
753
754 /*
755  * Return preferred size.  Happily accept anything >= our preferred size.
756  * (TODO: is that the right thing to do?  Should we always return "almost"
757  * if offered more than we need?)
758  */
759
760 static XtGeometryResult
761 TabsQueryGeometry(Widget w,
762                   XtWidgetGeometry * intended, XtWidgetGeometry * preferred)
763 {
764         register TabsWidget tw = (TabsWidget) w;
765         XtGeometryMask mode = intended->request_mode;
766
767         preferred->request_mode = CWWidth | CWHeight;
768         PreferredSize(tw, &preferred->width, &preferred->height, NULL, NULL);
769
770         if ((!(mode & CWWidth) || intended->width == w->core.width) &&
771             (!(mode & CWHeight) || intended->height == w->core.height))
772                 return XtGeometryNo;
773
774         if ((!(mode & CWWidth) || intended->width >= preferred->width) &&
775             (!(mode & CWHeight) || intended->height >= preferred->height))
776                 return XtGeometryYes;
777
778         return XtGeometryAlmost;
779 }
780
781 /*
782  * Geometry Manager; called when a child wants to be resized.
783  */
784
785 static XtGeometryResult
786 TabsGeometryManager(Widget w, XtWidgetGeometry * req, XtWidgetGeometry * reply)
787 {
788         TabsWidget tw = (TabsWidget) XtParent(w);
789         Dimension s = SHADWID;
790         TabsConstraints tab = (TabsConstraints) w->core.constraints;
791         XtGeometryResult result;
792         Dimension rw, rh;
793
794         /* Position request always denied */
795
796         if (((req->request_mode & CWX) && req->x != w->core.x) ||
797             ((req->request_mode & CWY) && req->y != w->core.y) ||
798             !tab->tabs.resizable)
799                 return XtGeometryNo;
800
801         /* Make all three fields in the request valid */
802         if (!(req->request_mode & CWWidth))
803                 req->width = w->core.width;
804         if (!(req->request_mode & CWHeight))
805                 req->height = w->core.height;
806         if (!(req->request_mode & CWBorderWidth))
807                 req->border_width = w->core.border_width;
808
809         if (req->width == w->core.width &&
810             req->height == w->core.height &&
811             req->border_width == w->core.border_width)
812                 return XtGeometryNo;
813
814         rw = req->width + 2 * req->border_width;
815         rh = req->height + 2 * req->border_width;
816
817         /* find out how big the children want to be now */
818         MaxChild(tw, w, rw, rh);
819
820         /* Size changes must see if the new size can be accommodated.
821          * The Tabs widget keeps all of its children the same
822          * size.  A request to shrink will be accepted only if the
823          * new size is still big enough for all other children.  A
824          * request to shrink that is not big enough for all children
825          * returns an "almost" response with the new proposed size
826          * or a "no" response if unable to shrink at all.
827          *
828          * A request to grow will be accepted only if the Tabs parent can
829          * grow to accommodate.
830          *
831          * TODO:
832          * We could get fancy here and re-arrange the tabs if it is
833          * necessary to compromise with the parent, but we'll save that
834          * for another day.
835          */
836
837         if (req->request_mode & (CWWidth | CWHeight | CWBorderWidth)) {
838                 Dimension cw, ch;       /* children's preferred size */
839                 Dimension aw, ah;       /* available size we can give child */
840                 Dimension th;   /* space used by tabs */
841                 Dimension wid, hgt;     /* Tabs widget size */
842
843                 cw = tw->tabs.max_cw;
844                 ch = tw->tabs.max_ch;
845
846                 /* find out what *my* resulting preferred size would be */
847
848                 PreferredSize2(tw, cw, ch, &wid, &hgt);
849
850                 /* Would my size change?  If so, ask to be resized. */
851
852                 if (wid != tw->core.width || hgt != tw->core.height) {
853                         Dimension oldWid = tw->core.width, oldHgt =
854                             tw->core.height;
855                         XtWidgetGeometry myrequest, myreply;
856
857                         myrequest.width = wid;
858                         myrequest.height = hgt;
859                         myrequest.request_mode = CWWidth | CWHeight;
860
861                         assert(wid > 0 && hgt > 0);
862                         /* If child is only querying, or if we're going to have to
863                          * offer the child a compromise, then make this a query only.
864                          */
865
866                         if ((req->request_mode & XtCWQueryOnly) || rw < cw
867                             || rh < ch)
868                                 myrequest.request_mode |= XtCWQueryOnly;
869
870                         result =
871                             XtMakeGeometryRequest((Widget) tw, &myrequest,
872                                                   &myreply);
873
874                         /* !$@# Athena Box widget changes the core size even if QueryOnly
875                          * is set.  I'm convinced this is a bug.  At any rate, to work
876                          * around the bug, we need to restore the core size after every
877                          * query geometry request.  This is only partly effective,
878                          * as there may be other boxes further up the tree.
879                          */
880                         if (myrequest.request_mode & XtCWQueryOnly) {
881                                 tw->core.width = oldWid;
882                                 tw->core.height = oldHgt;
883                         }
884
885                         /* based on the parent's response, determine what the
886                          * resulting Tabs widget size would be.
887                          */
888
889                         switch (result) {
890                         case XtGeometryYes:
891                         case XtGeometryDone:
892                                 tw->tabs.needs_layout = True;
893                                 break;
894
895                         case XtGeometryNo:
896                                 wid = tw->core.width;
897                                 hgt = tw->core.height;
898                                 break;
899
900                         case XtGeometryAlmost:
901                                 wid = myreply.width;
902                                 hgt = myreply.height;
903                                 tw->tabs.needs_layout = True;
904                                 break;
905                         default:
906                                 break;
907                         }
908                 }
909
910                 /* Within the constraints imposed by the parent, what is
911                  * the max size we can give the child?
912                  */
913                 (void)TabLayout(tw, wid, hgt, &th, True);
914                 aw = wid - 2 * s;
915                 ah = hgt - th - 2 * s;
916
917                 /* OK, make our decision.  If requested size is >= max sibling
918                  * preferred size, AND requested size <= available size, then
919                  * we accept.  Otherwise, we offer a compromise.
920                  */
921
922                 if (rw == aw && rh == ah) {
923                         /* Acceptable.  If this wasn't a query, change *all* children
924                          * to this size.
925                          */
926                         if (req->request_mode & XtCWQueryOnly)
927                                 return XtGeometryYes;
928                         else {
929                                 Widget *childP = tw->composite.children;
930                                 int i, bw;
931                                 w->core.border_width = req->border_width;
932                                 for (i = TabsNumChildren(tw); --i >= 0;
933                                      ++childP)
934                                         if (TabVisible(*childP)) {
935                                                 bw = (*childP)->core.
936                                                     border_width;
937                                                 XtConfigureWidget(*childP, s,
938                                                                   tw->tabs.
939                                                                   tab_total + s,
940                                                                   rw - 2 * bw,
941                                                                   rh - 2 * bw,
942                                                                   bw);
943                                         }
944 #ifdef  COMMENT
945                                 /* TODO: under what conditions will we need to redraw? */
946                                 XClearWindow(XtDisplay((Widget) tw),
947                                              XtWindow((Widget) tw));
948                                 XtClass(tw)->core_class.expose((Widget) tw,
949                                                                NULL, NULL);
950 #endif                          /* COMMENT */
951                                 return XtGeometryDone;
952                         }
953                 }
954
955                 /* Cannot grant child's request.  Describe what we *can* do
956                  * and return counter-offer.
957                  */
958                 reply->width = aw - 2 * req->border_width;
959                 reply->height = ah - 2 * req->border_width;
960                 reply->border_width = req->border_width;
961                 reply->request_mode = CWWidth | CWHeight | CWBorderWidth;
962                 return XtGeometryAlmost;
963         }
964
965         return XtGeometryYes;
966 }
967
968         /* The number of children we manage has changed; recompute
969          * size from scratch.
970          */
971
972 static void TabsChangeManaged(Widget w)
973 {
974         TabsWidget tw = (TabsWidget) w;
975         Widget *childP = tw->composite.children;
976         int i;
977
978         if (tw->tabs.topWidget != NULL &&
979             (!XtIsManaged(tw->tabs.topWidget) ||
980              tw->tabs.topWidget->core.being_destroyed))
981                 tw->tabs.topWidget = NULL;
982
983         /* Check whether the highlight tab is still valid. */
984         if (tw->tabs.hilight != NULL &&
985             (!XtIsManaged(tw->tabs.hilight) ||
986              tw->tabs.hilight->core.being_destroyed))
987                 tw->tabs.hilight = NULL;
988
989         GetPreferredSizes(tw);
990         MakeSizeRequest(tw);
991
992         XtClass(w)->core_class.resize(w);
993         if (XtIsRealized(w)) {
994                 Display *dpy = XtDisplay(w);
995                 XClearWindow(dpy, XtWindow(w));
996                 XtClass(w)->core_class.expose(w, NULL, NULL);
997
998                 /* make sure the top widget stays on top.  This requires
999                  * making sure that all new children are realized first.
1000                  */
1001                 if (tw->tabs.topWidget != NULL
1002                     && XtIsRealized(tw->tabs.topWidget)) {
1003                         for (i = TabsNumChildren(tw); --i >= 0; ++childP)
1004                                 if (!XtIsRealized(*childP))
1005                                         XtRealizeWidget(*childP);
1006
1007                         XRaiseWindow(dpy, XtWindow(tw->tabs.topWidget));
1008                 }
1009         }
1010 #ifdef  NEED_MOTIF
1011         /* Only top widget may receive input */
1012
1013         for (childP = tw->composite.children, i = tw->composite.num_children;
1014              --i >= 0; ++childP) {
1015                 XtVaSetValues(*childP, XmNtraversalOn, False, 0);
1016         }
1017
1018         if (tw->tabs.topWidget != NULL)
1019                 XtVaSetValues(tw->tabs.topWidget, XmNtraversalOn, True, 0);
1020 #endif
1021 }
1022 \f
1023 /****************************************************************
1024  *
1025  * Action Procedures
1026  *
1027  ****************************************************************/
1028
1029         /* User clicks on a tab, figure out which one it was. */
1030
1031 /* ARGSUSED */
1032 static void
1033 TabsSelect(Widget w, XEvent * event, String * params, Cardinal * num_params)
1034 {
1035         TabsWidget tw = (TabsWidget) w;
1036         Widget *childP;
1037         Position x, y;
1038         Dimension h = tw->tabs.tab_height;
1039         unsigned int i;
1040
1041 #ifdef  NEED_MOTIF
1042         XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1043 #endif
1044
1045         /* TODO: is there an Xmu function or something to do this instead? */
1046         switch (event->type) {
1047         case ButtonPress:
1048         case ButtonRelease:
1049                 x = event->xbutton.x;
1050                 y = event->xbutton.y;
1051                 break;
1052         case KeyPress:
1053         case KeyRelease:
1054                 x = event->xkey.x;
1055                 y = event->xkey.y;
1056                 break;
1057         default:
1058                 return;
1059         }
1060
1061         /* TODO: determine which tab was clicked, if any.  Set that
1062          * widget to be top of stacking order with XawTabsSetTop().
1063          */
1064         for (i = 0, childP = tw->composite.children;
1065              i < TabsNumChildren(tw); ++i, ++childP)
1066                 if (TabVisible(*childP)) {
1067                         TabsConstraints tab =
1068                             (TabsConstraints) (*childP)->core.constraints;
1069                         if (x > tab->tabs.x && x < tab->tabs.x + tab->tabs.width
1070                             && y > tab->tabs.y && y < tab->tabs.y + h) {
1071                                 if (*childP != tw->tabs.topWidget
1072                                     && (XtIsSensitive(*childP)
1073                                         || tw->tabs.selectInsensitive))
1074                                         XawTabsSetTop(*childP, True);
1075                                 break;
1076                         }
1077                 }
1078 }
1079
1080         /* User hits a key */
1081
1082 static void
1083 TabsPage(Widget w, XEvent * event, String * params, Cardinal * num_params)
1084 {
1085         TabsWidget tw = (TabsWidget) w;
1086         Widget newtop = NULL;
1087         Widget *childP;
1088         int idx;
1089         int nc = TabsNumChildren(tw);
1090
1091         if (nc <= 0)
1092                 return;
1093
1094         if (*num_params < 1) {
1095                 XtAppWarning(XtWidgetToApplicationContext(w),
1096                              "Tabs: page() action called with no arguments");
1097                 return;
1098         }
1099
1100         if (tw->tabs.topWidget == NULL)
1101                 tw->tabs.topWidget = tw->composite.children[0];
1102
1103         for (idx = 0, childP = tw->composite.children; idx < nc;
1104              ++idx, ++childP)
1105                 if (tw->tabs.topWidget == *childP)
1106                         break;
1107
1108         switch (params[0][0]) {
1109         case 'u':               /* up */
1110         case 'U':
1111                 if (--idx < 0)
1112                         idx = nc - 1;
1113                 newtop = tw->composite.children[idx];
1114                 break;
1115
1116         case 'd':               /* down */
1117         case 'D':
1118                 if (++idx >= nc)
1119                         idx = 0;
1120                 newtop = tw->composite.children[idx];
1121                 break;
1122
1123         case 'h':
1124         case 'H':
1125         default:
1126                 newtop = tw->composite.children[0];
1127                 break;
1128
1129         case 'e':
1130         case 'E':
1131                 newtop = tw->composite.children[nc - 1];
1132                 break;
1133
1134         case 's':               /* selected */
1135         case 'S':
1136                 if ((newtop = tw->tabs.hilight) == NULL)
1137                         return;
1138                 break;
1139         }
1140
1141         XawTabsSetTop(newtop, True);
1142 }
1143
1144         /* User hits up/down key */
1145
1146 static void
1147 TabsHighlight(Widget w, XEvent * event, String * params, Cardinal * num_params)
1148 {
1149         TabsWidget tw = (TabsWidget) w;
1150         Widget newhl = NULL;
1151         Widget *childP;
1152         int idx;
1153         int nc = TabsNumChildren(tw);
1154
1155         if (nc <= 0)
1156                 return;
1157
1158         if (*num_params < 1) {
1159                 if (tw->tabs.hilight != NULL)
1160                         DrawHighlight(tw, tw->tabs.hilight, False);
1161                 return;
1162         }
1163
1164         if (tw->tabs.hilight == NULL)
1165                 newhl = tw->composite.children[0];
1166
1167         else {
1168                 /* find index of currently highlit child */
1169                 for (idx = 0, childP = tw->composite.children; idx < nc;
1170                      ++idx, ++childP)
1171                         if (tw->tabs.hilight == *childP)
1172                                 break;
1173
1174                 switch (params[0][0]) {
1175                 case 'u':       /* up */
1176                 case 'U':
1177                         if (--idx < 0)
1178                                 idx = nc - 1;
1179                         newhl = tw->composite.children[idx];
1180                         break;
1181
1182                 case 'd':       /* down */
1183                 case 'D':
1184                         if (++idx >= nc)
1185                                 idx = 0;
1186                         newhl = tw->composite.children[idx];
1187                         break;
1188
1189                 case 'h':
1190                 case 'H':
1191                         newhl = tw->composite.children[0];
1192                         break;
1193
1194                 case 'e':
1195                 case 'E':
1196                         newhl = tw->composite.children[nc - 1];
1197                         break;
1198
1199                 default:
1200                         newhl = tw->tabs.hilight;
1201                         break;
1202                 }
1203         }
1204
1205         XawTabsSetHighlight(w, newhl);
1206 }
1207
1208 static void
1209 TabsUnhighlight(Widget w, XEvent * event, String * params,
1210                 Cardinal * num_params)
1211 {
1212         TabsWidget tw = (TabsWidget) w;
1213         int nc = tw->composite.num_children;
1214
1215         if (nc <= 0)
1216                 return;
1217
1218         if (tw->tabs.hilight != NULL)
1219                 DrawHighlight(tw, tw->tabs.hilight, True);
1220 }
1221 \f
1222 /****************************************************************
1223  *
1224  * Public Procedures
1225  *
1226  ****************************************************************/
1227
1228         /* Set the top tab, optionally call all callbacks. */
1229 void XawTabsSetTop(Widget w, Bool callCallbacks)
1230 {
1231         TabsWidget tw = (TabsWidget) w->core.parent;
1232         TabsConstraints tab;
1233         Widget oldtop = tw->tabs.topWidget;
1234
1235         if (!XtIsSubclass(w->core.parent, tabsWidgetClass)) {
1236                 char line[256];
1237                 int n = snprintf(line, sizeof(line),
1238                         "XawTabsSetTop: widget \"%.64s\" is not the child of a tabs widget.",
1239                         XtName(w));
1240                 assert(n >= 0 && (size_t)n < sizeof(line));
1241                 XtAppWarning(XtWidgetToApplicationContext(w), line);
1242                 return;
1243         }
1244
1245         if (callCallbacks)
1246                 XtCallCallbackList(w, tw->tabs.popdownCallbacks,
1247                                    (XtPointer) tw->tabs.topWidget);
1248
1249         if (!XtIsRealized(w)) {
1250                 tw->tabs.topWidget = w;
1251                 tw->tabs.needs_layout = True;
1252                 tw->tabs.hilight = NULL;        /* The highlight tab might disappear. */
1253                 return;
1254         }
1255
1256         XRaiseWindow(XtDisplay(w), XtWindow(w));
1257 #ifdef  NEED_MOTIF
1258         XtVaSetValues(oldtop, XmNtraversalOn, False, 0);
1259         XtVaSetValues(w, XmNtraversalOn, True, 0);
1260 #endif
1261
1262         tab = (TabsConstraints) w->core.constraints;
1263
1264         /* Unhighlight before we start messing with the stacking order. */
1265         if (tw->tabs.hilight != NULL) {
1266                 DrawHighlight(tw, tw->tabs.hilight, True);
1267                 tw->tabs.hilight = NULL;
1268         }
1269
1270         if (tab->tabs.row == 0) {
1271                 /* Easy case; undraw current top, undraw new top, assign new
1272                  * top, redraw all borders.
1273                  * We *could* just erase and execute a full redraw, but I like to
1274                  * reduce screen flicker.
1275                  */
1276                 UndrawTab(tw, oldtop);  /* undraw old */
1277                 DrawBorder(tw, oldtop, True);
1278                 UndrawTab(tw, w);       /* undraw new */
1279                 DrawBorder(tw, w, True);
1280                 tw->tabs.topWidget = w;
1281                 DrawTab(tw, oldtop, True);      /* redraw old */
1282                 DrawTab(tw, w, True);   /* redraw new */
1283                 DrawTabs(tw, False);
1284         } else {
1285                 tw->tabs.topWidget = w;
1286                 TabsShuffleRows(tw);
1287                 XClearWindow(XtDisplay((Widget) tw), XtWindow((Widget) tw));
1288                 XtClass(tw)->core_class.expose((Widget) tw, NULL, None);
1289         }
1290
1291         XawTabsSetHighlight((Widget) tw, w);
1292
1293         if (callCallbacks)
1294                 XtCallCallbackList(w, tw->tabs.callbacks, (XtPointer) w);
1295 }
1296
1297         /* Set the top tab, optionally call all callbacks. */
1298 void XawTabsSetHighlight(Widget t, Widget w)
1299 {
1300         TabsWidget tw = (TabsWidget) t;
1301
1302         if (!XtIsSubclass(t, tabsWidgetClass))
1303                 return;
1304
1305         if (XtIsRealized(t) && w != tw->tabs.hilight) {
1306                 if (w != NULL)
1307                         DrawHighlight(tw, w, False);
1308         }
1309
1310         tw->tabs.hilight = w;
1311 }
1312 \f
1313 /****************************************************************
1314  *
1315  * Private Procedures
1316  *
1317  ****************************************************************/
1318
1319 static void TabsAllocGCs(TabsWidget tw)
1320 {
1321         TabsAllocFgGC(tw);
1322         TabsAllocGreyGC(tw);
1323         tw->tabs.backgroundGC = AllocBackgroundGC((Widget) tw, None);
1324         tw->tabs.topGC = AllocTopShadowGC((Widget) tw,
1325                                           tw->tabs.top_shadow_contrast,
1326                                           tw->tabs.be_nice_to_cmap);
1327         tw->tabs.botGC =
1328             AllocBotShadowGC((Widget) tw, tw->tabs.bot_shadow_contrast,
1329                              tw->tabs.be_nice_to_cmap);
1330 }
1331
1332 static void TabsFreeGCs(TabsWidget tw)
1333 {
1334         Widget w = (Widget) tw;
1335
1336         XtReleaseGC(w, tw->tabs.foregroundGC);
1337         XtReleaseGC(w, tw->tabs.greyGC);
1338         XtReleaseGC(w, tw->tabs.backgroundGC);
1339         XtReleaseGC(w, tw->tabs.topGC);
1340         XtReleaseGC(w, tw->tabs.botGC);
1341 #ifdef HAVE_XMU
1342         XmuReleaseStippledPixmap(XtScreen(w), tw->tabs.grey50);
1343 #endif
1344 }
1345
1346         /* Redraw entire Tabs widget */
1347
1348 static void DrawTabs(TabsWidget tw, Bool labels)
1349 {
1350         Widget *childP;
1351         unsigned int i;
1352         int j;
1353         Dimension s = SHADWID;
1354         Dimension th = tw->tabs.tab_height;
1355         Position y;
1356         TabsConstraints tab;
1357
1358         if (!XtIsRealized((Widget) tw))
1359                 return;
1360
1361         /* draw tabs and frames by row except for the top tab, which
1362          * is drawn last.  (This is inefficiently written, but should not
1363          * be too slow as long as there are not a lot of rows.)
1364          */
1365
1366         y = tw->tabs.numRows == 1 ? TABDELTA : 0;
1367         for (i = 0; i < tw->tabs.numRows; ++i, y += th) {
1368                 for (j = TabsNumChildren(tw), childP = tw->composite.children;
1369                      --j >= 0; ++childP)
1370                         if (TabVisible(*childP)) {
1371                                 tab =
1372                                     (TabsConstraints) (*childP)->core.
1373                                     constraints;
1374                                 if (tab->tabs.row == (int)i
1375                                     && *childP != tw->tabs.topWidget)
1376                                         DrawTab(tw, *childP, labels);
1377                         }
1378                 if (i != tw->tabs.numRows - 1)
1379                         DrawTrim(tw, 0, y + th, tw->core.width, th + s, False,
1380                                  False);
1381         }
1382
1383         DrawFrame(tw);
1384
1385         /* and now the top tab */
1386         if (tw->tabs.topWidget != NULL)
1387                 DrawTab(tw, tw->tabs.topWidget, labels);
1388 }
1389
1390 /* Draw one tab.  Corners are rounded very slightly. */
1391
1392 static void DrawTab(TabsWidget tw, Widget child, Bool labels)
1393 {
1394         GC gc;
1395         int x, y;
1396
1397         if (!XtIsRealized((Widget) tw))
1398                 return;
1399
1400         DrawBorder(tw, child, False);
1401
1402         if (labels) {
1403                 TabsConstraints tab = (TabsConstraints) child->core.constraints;
1404                 Display *dpy = XtDisplay((Widget) tw);
1405                 Window win = XtWindow((Widget) tw);
1406                 String lbl = tab->tabs.label != NULL ?
1407                     tab->tabs.label : XtName(child);
1408
1409                 if (XtIsSensitive(child)) {
1410                         gc = tw->tabs.foregroundGC;
1411                         XSetForeground(dpy, gc, tab->tabs.foreground);
1412                 } else {
1413                         /* grey pixel allocation deferred until now */
1414                         if (!tab->tabs.greyAlloc) {
1415                                 if (tw->tabs.be_nice_to_cmap
1416                                     || tw->core.depth == 1)
1417                                         tab->tabs.grey = tab->tabs.foreground;
1418                                 else
1419                                         tab->tabs.grey =
1420                                             AllocGreyPixel((Widget) tw,
1421                                                            tab->tabs.foreground,
1422                                                            tw->core.
1423                                                            background_pixel,
1424                                                            tw->tabs.
1425                                                            insensitive_contrast);
1426                                 tab->tabs.greyAlloc = True;
1427                         }
1428                         gc = tw->tabs.greyGC;
1429                         XSetForeground(dpy, gc, tab->tabs.grey);
1430                 }
1431
1432                 x = tab->tabs.x;
1433                 y = tab->tabs.y;
1434                 if (child == tw->tabs.topWidget)
1435                         y -= TABLDELTA;
1436
1437                 if (tab->tabs.left_bitmap != None && tab->tabs.lbm_width > 0) {
1438                         if (tab->tabs.lbm_depth == 1)
1439                                 XCopyPlane(dpy, tab->tabs.left_bitmap, win, gc,
1440                                            0, 0, tab->tabs.lbm_width,
1441                                            tab->tabs.lbm_height,
1442                                            x + tab->tabs.lbm_x,
1443                                            y + tab->tabs.lbm_y, 1L);
1444                         else
1445                                 XCopyArea(dpy, tab->tabs.left_bitmap, win, gc,
1446                                           0, 0, tab->tabs.lbm_width,
1447                                           tab->tabs.lbm_height,
1448                                           x + tab->tabs.lbm_x,
1449                                           y + tab->tabs.lbm_y);
1450                 }
1451
1452                 if (lbl != NULL && tw->tabs.font != NULL)
1453                         XDrawString(dpy, win, gc,
1454                                     x + tab->tabs.l_x, y + tab->tabs.l_y,
1455                                     lbl, (int)strlen(lbl));
1456         }
1457
1458         if (child == tw->tabs.hilight)
1459                 DrawHighlight(tw, child, False);
1460 }
1461
1462         /* draw frame all the way around the child windows. */
1463
1464 static void DrawFrame(TabsWidget tw)
1465 {
1466         GC topgc = tw->tabs.topGC;
1467         GC botgc = tw->tabs.botGC;
1468         Dimension s = SHADWID;
1469         Dimension ch = tw->tabs.child_height;
1470         if (ch > 0)
1471                 Draw3dBox((Widget) tw, 0, tw->tabs.tab_total,
1472                           tw->core.width, ch + 2 * s, s, topgc, botgc);
1473         else {
1474                 Widget w = tw->tabs.topWidget;
1475                 if (w != NULL) {
1476                         TabsConstraints tab =
1477                             (TabsConstraints) w->core.constraints;
1478                         Draw3dBox((Widget) tw, 0, tw->core.height - 2 * s,
1479                                   tab->tabs.x, 2 * s, s, topgc, botgc);
1480                         Draw3dBox((Widget) tw, tab->tabs.x + tab->tabs.width,
1481                                   tw->core.height - 2 * s,
1482                                   tw->core.width - tab->tabs.x -
1483                                   tab->tabs.width, 2 * s, s, topgc, botgc);
1484                 } else
1485                         Draw3dBox((Widget) tw, 0, tw->core.height - 2 * s,
1486                                   tw->core.width, 2 * s, s, topgc, botgc);
1487         }
1488 }
1489
1490         /* draw trim around a tab or underneath a row of tabs */
1491
1492 static void DrawTrim(TabsWidget tw,     /* widget */
1493                      int x,     /* upper-left corner */
1494                      int y, int wid,    /* total size */
1495                      int hgt, Bool bottom,      /* draw bottom? */
1496                      Bool undraw)
1497 {                               /* undraw all */
1498         Display *dpy = XtDisplay((Widget) tw);
1499         Window win = XtWindow((Widget) tw);
1500         GC bggc = tw->tabs.backgroundGC;
1501         GC topgc = undraw ? bggc : tw->tabs.topGC;
1502         GC botgc = undraw ? bggc : tw->tabs.botGC;
1503         if (bottom)
1504                 XDrawLine(dpy, win, bggc, x, y + hgt - 1, x + wid - 1, y + hgt - 1);    /* bottom */
1505         XDrawLine(dpy, win, topgc, x, y + 2, x, y + hgt - 2);   /* left */
1506         XDrawPoint(dpy, win, topgc, x + 1, y + 1);      /* corner */
1507         XDrawLine(dpy, win, topgc, x + 2, y, x + wid - 3, y);   /* top */
1508         XDrawLine(dpy, win, botgc, x + wid - 2, y + 1, x + wid - 2, y + hgt - 2);       /* right */
1509         XDrawLine(dpy, win, botgc, x + wid - 1, y + 2, x + wid - 1, y + hgt - 2);       /* right */
1510 }
1511
1512 /* Draw one tab border. */
1513
1514 static void DrawBorder(TabsWidget tw, Widget child, Bool undraw)
1515 {
1516         TabsConstraints tab = (TabsConstraints) child->core.constraints;
1517         Position x = tab->tabs.x;
1518         Position y = tab->tabs.y;
1519         Dimension twid = tab->tabs.width;
1520         Dimension thgt = tw->tabs.tab_height;
1521
1522         /* top tab requires a little special attention; it overlaps
1523          * neighboring tabs slightly, so the background must be cleared
1524          * in the region of the overlap to partially erase those neighbors.
1525          * TODO: is this worth doing with regions instead?
1526          */
1527         if (child == tw->tabs.topWidget) {
1528                 Display *dpy = XtDisplay((Widget) tw);
1529                 Window win = XtWindow((Widget) tw);
1530                 GC bggc = tw->tabs.backgroundGC;
1531                 XRectangle rects[3];
1532                 x -= TABDELTA;
1533                 y -= TABDELTA;
1534                 twid += TABDELTA * 2;
1535                 thgt += TABDELTA;
1536                 AddRect(0, x, y + 1, twid, TABDELTA);
1537                 AddRect(1, x + 1, y, TABDELTA, thgt);
1538                 AddRect(2, x + twid - TABDELTA - 1, y, TABDELTA, thgt);
1539                 XFillRectangles(dpy, win, bggc, rects, 3);
1540         }
1541
1542         DrawTrim(tw, x, y, twid, thgt + 1, child == tw->tabs.topWidget, undraw);
1543 }
1544
1545 /* Draw highlight around tab that has focus */
1546
1547 static void DrawHighlight(TabsWidget tw, Widget child, Bool undraw)
1548 {
1549         TabsConstraints tab = (TabsConstraints) child->core.constraints;
1550         Display *dpy = XtDisplay((Widget) tw);
1551         Window win = XtWindow((Widget) tw);
1552         GC gc;
1553         Position x = tab->tabs.x;
1554         Position y = tab->tabs.y;
1555         Dimension wid = tab->tabs.width;
1556         Dimension hgt = tw->tabs.tab_height;
1557         XPoint points[6];
1558
1559         /* top tab does not have a highlight */
1560
1561         if (child == tw->tabs.topWidget)
1562                 return;
1563
1564         if (undraw)
1565                 gc = tw->tabs.backgroundGC;
1566
1567         else if (XtIsSensitive(child)) {
1568                 gc = tw->tabs.foregroundGC;
1569                 XSetForeground(dpy, gc, tab->tabs.foreground);
1570         } else {
1571                 gc = tw->tabs.greyGC;
1572                 XSetForeground(dpy, gc, tab->tabs.grey);
1573         }
1574
1575         points[0].x = x + 1;
1576         points[0].y = y + hgt - 1;
1577         points[1].x = x + 1;
1578         points[1].y = y + 2;
1579         points[2].x = x + 2;
1580         points[2].y = y + 1;
1581         points[3].x = x + wid - 4;
1582         points[3].y = y + 1;
1583         points[4].x = x + wid - 3;
1584         points[4].y = y + 2;
1585         points[5].x = x + wid - 3;
1586         points[5].y = y + hgt - 1;
1587
1588         XDrawLines(dpy, win, gc, points, 6, CoordModeOrigin);
1589 }
1590
1591 /* Undraw one tab interior */
1592
1593 static void UndrawTab(TabsWidget tw, Widget child)
1594 {
1595         TabsConstraints tab = (TabsConstraints) child->core.constraints;
1596         Position x = tab->tabs.x;
1597         Position y = tab->tabs.y;
1598         Dimension twid = tab->tabs.width;
1599         Dimension thgt = tw->tabs.tab_height;
1600         Display *dpy = XtDisplay((Widget) tw);
1601         Window win = XtWindow((Widget) tw);
1602         GC bggc = tw->tabs.backgroundGC;
1603
1604         XFillRectangle(dpy, win, bggc, x, y, twid, thgt);
1605 }
1606 \f
1607         /* GEOMETRY UTILITIES */
1608
1609         /* Overview:
1610          *
1611          *  MaxChild(): ask all children (except possibly one) their
1612          *  preferred sizes, set max_cw, max_ch accordingly.
1613          *
1614          *  GetPreferredSizes(): ask all children their preferred sizes,
1615          *  set max_cw, max_ch accordingly.
1616          *
1617          *  PreferredSize(): given max_cw, max_ch, return tabs widget
1618          *  preferred size.  Iterate with other widths in order to get
1619          *  a reasonable aspect ratio.
1620          *
1621          *  PreferredSize2(): Given child dimensions, return Tabs
1622          *  widget dimensions.
1623          *
1624          *  PreferredSize3(): Same, except given child dimensions plus
1625          *  shadow.
1626          */
1627
1628         /* Compute the width of one child's tab.  Positions will be computed
1629          * elsewhere.
1630          *
1631          *      height: font height + vertical_space*2 + shadowWid*2
1632          *      width:  string width + horizontal_space*2 + shadowWid*2
1633          *
1634          * All tabs are the same height, so that is computed elsewhere.
1635          */
1636
1637 static void TabWidth(Widget w)
1638 {
1639         TabsConstraints tab = (TabsConstraints) w->core.constraints;
1640         TabsWidget tw = (TabsWidget) XtParent(w);
1641         String lbl = tab->tabs.label != NULL ? tab->tabs.label : XtName(w);
1642         XFontStruct *font = tw->tabs.font;
1643         int iw = tw->tabs.internalWidth;
1644
1645         tab->tabs.width = iw + SHADWID * 2;
1646         tab->tabs.l_x = tab->tabs.lbm_x = SHADWID + iw;
1647
1648         if (tab->tabs.left_bitmap != None) {
1649                 tab->tabs.width += tab->tabs.lbm_width + iw;
1650                 tab->tabs.l_x += tab->tabs.lbm_width + iw;
1651                 tab->tabs.lbm_y =
1652                     (tw->tabs.tab_height - tab->tabs.lbm_height) / 2;
1653         }
1654
1655         if (lbl != NULL && font != NULL) {
1656                 tab->tabs.width += XTextWidth(font, lbl, (int)strlen(lbl)) + iw;
1657                 tab->tabs.l_y = (tw->tabs.tab_height +
1658                                  tw->tabs.font->max_bounds.ascent -
1659                                  tw->tabs.font->max_bounds.descent) / 2;
1660         }
1661 }
1662
1663         /* Lay out tabs to fit in given width.  Compute x,y position and
1664          * row number for each tab.  Return number of rows and total height
1665          * required by all tabs.  If there is only one row, add TABDELTA
1666          * height to the total.  Rows are assigned bottom to top.
1667          *
1668          * Tabs are indented from the edges by INDENT.
1669          *
1670          * TODO: if they require more than two rows and the total height:width
1671          * ratio is more than 2:1, then try something else.
1672          */
1673
1674 static int
1675 TabLayout(TabsWidget tw,
1676           Dimension wid,
1677           Dimension hgt, Dimension * reply_height, Bool query_only)
1678 {
1679         int i, row, done = 0, display_rows = 0;
1680         int num_children = tw->composite.num_children;
1681         Widget *childP;
1682         Dimension w;
1683         Position x, y;
1684         TabsConstraints tab;
1685
1686         /* Algorithm: loop through children, assign X positions.  If a tab
1687          * would extend beyond the right edge, start a new row.  After all
1688          * rows are assigned, make a second pass and assign Y positions.
1689          */
1690
1691         if (num_children > 0) {
1692                 /* Loop through the tabs and see how much space they need. */
1693
1694                 row = 0;
1695                 x = INDENT;
1696                 y = 0;
1697                 wid -= INDENT;
1698                 for (i = num_children, childP = tw->composite.children;
1699                      --i >= 0; ++childP)
1700                         if (XtIsManaged(*childP)) {
1701                                 tab =
1702                                     (TabsConstraints) (*childP)->core.
1703                                     constraints;
1704                                 w = tab->tabs.width;
1705
1706                                 if (x + w > wid) {      /* new row */
1707                                         if (y + tw->tabs.tab_height > hgt
1708                                             && !done) {
1709                                                 display_rows = row;
1710                                                 done = 1;
1711                                         }
1712                                         ++row;
1713                                         x = INDENT;
1714                                         y += tw->tabs.tab_height;
1715                                 }
1716                                 if (!query_only) {
1717                                         tab->tabs.x = x;
1718                                         tab->tabs.y = y;
1719                                         tab->tabs.row = row;
1720                                 }
1721                                 x += w + SPACING;
1722                                 if (!query_only && !done)
1723                                         tab->tabs.visible = 1;
1724
1725                         }
1726                 /* If there was only one row, increase the height by TABDELTA */
1727                 if (++display_rows == 1) {
1728                         row++;
1729                         y = TABDELTA;
1730                         if (!query_only)
1731                                 for (i = num_children, childP =
1732                                      tw->composite.children; --i >= 0; ++childP)
1733                                         if (XtIsManaged(*childP)) {
1734                                                 tab =
1735                                                     (TabsConstraints)
1736                                                     (*childP)->core.constraints;
1737                                                 tab->tabs.y = y;
1738                                         }
1739                 }
1740                 y += tw->tabs.tab_height;
1741         } else
1742                 display_rows = row = y = 0;
1743
1744         if (!query_only) {
1745                 tw->tabs.tab_total = y;
1746                 tw->tabs.numRows = display_rows;
1747                 tw->tabs.realRows = row;
1748         }
1749
1750         if (reply_height != NULL)
1751                 *reply_height = y;
1752
1753         return display_rows;
1754 }
1755
1756         /* Find max preferred child size.  Returned sizes include child
1757          * border widths.
1758          */
1759
1760 static void GetPreferredSizes(TabsWidget tw)
1761 {
1762         MaxChild(tw, NULL, 0, 0);
1763 }
1764
1765         /* Find max preferred child size.  Returned sizes include child
1766          * border widths.  If except is non-null, don't ask that one.
1767          */
1768
1769 static void MaxChild(TabsWidget tw, Widget except, Dimension cw, Dimension ch)
1770 {
1771         int i;
1772         Widget *childP = tw->composite.children;
1773         XtWidgetGeometry preferred;
1774
1775         for (i = tw->composite.num_children; --i >= 0; ++childP)
1776                 if (TabVisible(*childP) /*XtIsManaged(*childP) */ &&*childP !=
1777                     except) {
1778                         (void)XtQueryGeometry(*childP, NULL, &preferred);
1779                         cw = Max(cw,
1780                                  preferred.width + preferred.border_width * 2);
1781                         ch = Max(ch,
1782                                  preferred.height + preferred.border_width * 2);
1783                 }
1784
1785         tw->tabs.max_cw = cw;
1786         tw->tabs.max_ch = ch;
1787 }
1788
1789         /* rotate row numbers to bring current widget to bottom row,
1790          * compute y positions for all tabs
1791          */
1792
1793 static void TabsShuffleRows(TabsWidget tw)
1794 {
1795         TabsConstraints tab;
1796         int move;
1797         int real_rows, display_rows;
1798         Widget *childP;
1799         Dimension th = tw->tabs.tab_height;
1800         Position bottom;
1801         int i;
1802
1803         /* There must be a top widget.  If not, assign one. */
1804         if (tw->tabs.topWidget == NULL && tw->composite.children != NULL)
1805                 for (i = TabsNumChildren(tw), childP = tw->composite.children;
1806                      --i >= 0; ++childP)
1807                         if (XtIsManaged(*childP)) {
1808                                 tw->tabs.topWidget = *childP;
1809                                 break;
1810                         }
1811
1812         if (tw->tabs.topWidget != NULL) {
1813                 display_rows = tw->tabs.numRows;
1814                 real_rows = tw->tabs.realRows;
1815                 assert(display_rows <= real_rows);
1816
1817                 if (real_rows > 1) {
1818                         tab =
1819                             (TabsConstraints) tw->tabs.topWidget->core.
1820                             constraints;
1821                         assert(tab != NULL);
1822
1823                         /* How far to move top row. The selected tab must be on
1824                            the bottom row of the *visible* rows. */
1825                         move = (real_rows + 1 - display_rows) - tab->tabs.row;
1826                         if (move < 0)
1827                                 move = real_rows - move;
1828                         bottom = tw->tabs.tab_total - th;
1829
1830                         for (i = tw->composite.num_children, childP =
1831                              tw->composite.children; --i >= 0; ++childP)
1832                                 if (XtIsManaged(*childP)) {
1833                                         tab =
1834                                             (TabsConstraints) (*childP)->core.
1835                                             constraints;
1836                                         tab->tabs.row =
1837                                             (tab->tabs.row + move) % real_rows;
1838                                         tab->tabs.y =
1839                                             bottom - tab->tabs.row * th;
1840                                         tab->tabs.visible =
1841                                             (tab->tabs.row < display_rows);
1842                                 }
1843                 }
1844         }
1845 }
1846
1847         /* Find preferred size.  Ask children, find size of largest,
1848          * add room for tabs & return.  This can get a little involved,
1849          * as we don't want to have too many rows of tabs; we may widen
1850          * the widget to reduce # of rows.
1851          *
1852          * This function requires that max_cw, max_ch already be set.
1853          */
1854 static int PreferredSize(TabsWidget tw, Dimension * reply_width,        /* total widget size */
1855                          Dimension * reply_height, Dimension * reply_cw,        /* child widget size */
1856                          Dimension * reply_ch)
1857 {
1858         Dimension cw, ch;       /* child width, height */
1859         Dimension wid, hgt;
1860         Dimension rwid, rhgt;
1861         int nrow;
1862
1863         wid = cw = tw->tabs.max_cw;
1864         hgt = ch = tw->tabs.max_ch;
1865
1866         nrow = PreferredSize2(tw, wid, hgt, &rwid, &rhgt);
1867
1868         /* Check for absurd results (more than 2 rows, high aspect
1869          * ratio).  Try wider size if needed.
1870          * TODO: make sure this terminates.
1871          */
1872
1873         if (nrow > 2 && rhgt > rwid) {
1874                 Dimension w0, w1;
1875                 int maxloop = 20;
1876
1877                 /* step 1: start doubling size until it's too big */
1878                 do {
1879                         w0 = wid;
1880                         wid = Max(wid * 2, wid + 20);
1881                         nrow = PreferredSize2(tw, wid, hgt, &rwid, &rhgt);
1882                 } while (nrow > 2 && rhgt > rwid);
1883                 w1 = wid;
1884
1885                 /* step 2: use Newton's method to find ideal size.  Stop within
1886                  * 8 pixels.
1887                  */
1888                 while (--maxloop > 0 && w1 > w0 + 8) {
1889                         wid = (w0 + w1) / 2;
1890                         nrow = PreferredSize2(tw, wid, hgt, &rwid, &rhgt);
1891                         if (nrow > 2 && rhgt > rwid)
1892                                 w0 = wid;
1893                         else
1894                                 w1 = wid;
1895                 }
1896                 wid = w1;
1897         }
1898
1899         *reply_width = rwid;
1900         *reply_height = rhgt;
1901         if (reply_cw != NULL)
1902                 *reply_cw = cw;
1903         if (reply_ch != NULL)
1904                 *reply_ch = ch;
1905         return nrow;
1906 }
1907
1908         /* Find preferred size, given size of children. */
1909
1910 static int PreferredSize2(TabsWidget tw, Dimension cw,  /* child width, height */
1911                           Dimension ch, Dimension * reply_width,        /* total widget size */
1912                           Dimension * reply_height)
1913 {
1914         Dimension s = SHADWID;
1915         int ret;
1916
1917         /* make room for shadow frame */
1918         cw += s * 2;
1919         ch += s * 2;
1920
1921         ret = PreferredSize3(tw, cw, ch, reply_width, reply_height);
1922
1923         assert(*reply_width > 0 && *reply_height > 0);
1924         return ret;
1925 }
1926
1927         /* Find preferred size, given size of children+shadow. */
1928
1929 static int PreferredSize3(TabsWidget tw, Dimension wid, /* child width, height */
1930                           Dimension hgt, Dimension * reply_width,       /* total widget size */
1931                           Dimension * reply_height)
1932 {
1933         Dimension th;           /* space used by tabs */
1934         int nrows;
1935
1936         if (tw->composite.num_children > 0)
1937                 nrows = TabLayout(tw, wid, hgt, &th, True);
1938         else {
1939                 th = 0;
1940                 nrows = 0;
1941         }
1942
1943         *reply_width = Max(wid, MIN_WID);
1944         *reply_height = Max(th + hgt, MIN_HGT);
1945
1946         return nrows;
1947 }
1948
1949 static void MakeSizeRequest(TabsWidget tw)
1950 {
1951         Widget w = (Widget) tw;
1952         XtWidgetGeometry request, reply;
1953         XtGeometryResult result;
1954         Dimension cw, ch;
1955
1956         request.request_mode = CWWidth | CWHeight;
1957         PreferredSize(tw, &request.width, &request.height, &cw, &ch);
1958
1959         if (request.width == tw->core.width &&
1960             request.height == tw->core.height)
1961                 return;
1962
1963         result = XtMakeGeometryRequest(w, &request, &reply);
1964
1965         if (result == XtGeometryAlmost) {
1966                 /* Bugger.  Didn't get what we want, but were offered a
1967                  * compromise.  If the width was too small, recompute
1968                  * based on the too-small width and try again.
1969                  * If the height was too small, make a wild-ass guess
1970                  * at a wider width and try again.
1971                  */
1972
1973                 if (reply.width < request.width
1974                     && reply.height >= request.height) {
1975                         Dimension s = SHADWID;
1976                         ch += s * 2;
1977                         PreferredSize3(tw, reply.width, ch, &request.width,
1978                                        &request.height);
1979                         result = XtMakeGeometryRequest(w, &request, &reply);
1980                         if (result == XtGeometryAlmost)
1981                                 (void)XtMakeGeometryRequest(w, &reply, NULL);
1982                 }
1983
1984                 else
1985                         (void)XtMakeGeometryRequest(w, &reply, NULL);
1986         }
1987 }
1988
1989 static void getBitmapInfo(TabsWidget tw, TabsConstraints tab)
1990 {
1991         Window root;
1992         int x, y;
1993         unsigned int bw;
1994
1995         if (tab->tabs.left_bitmap == None ||
1996             !XGetGeometry(XtDisplay(tw), tab->tabs.left_bitmap, &root, &x, &y,
1997                           &tab->tabs.lbm_width, &tab->tabs.lbm_height,
1998                           &bw, &tab->tabs.lbm_depth))
1999                 tab->tabs.lbm_width = tab->tabs.lbm_height = 0;
2000 }
2001 \f
2002         /* Code copied & modified from Gcs.c.  This version has dynamic
2003          * foreground.
2004          */
2005
2006 static void TabsAllocFgGC(TabsWidget tw)
2007 {
2008         Widget w = (Widget) tw;
2009         XGCValues values;
2010
2011         values.background = tw->core.background_pixel;
2012         values.font = tw->tabs.font->fid;
2013         values.line_style = LineOnOffDash;
2014         values.line_style = LineSolid;
2015
2016         tw->tabs.foregroundGC =
2017             XtAllocateGC(w, w->core.depth,
2018                          GCBackground | GCFont | GCLineStyle, &values,
2019                          GCForeground,
2020                          GCSubwindowMode | GCGraphicsExposures | GCDashOffset |
2021                          GCDashList | GCArcMode);
2022 }
2023
2024 static void TabsAllocGreyGC(TabsWidget tw)
2025 {
2026         Widget w = (Widget) tw;
2027         XGCValues values;
2028
2029         values.background = tw->core.background_pixel;
2030         values.font = tw->tabs.font->fid;
2031 #ifdef HAVE_XMU
2032         if (tw->tabs.be_nice_to_cmap || w->core.depth == 1) {
2033                 values.fill_style = FillStippled;
2034                 tw->tabs.grey50 =
2035                     values.stipple =
2036                     XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1);
2037
2038                 tw->tabs.greyGC =
2039                     XtAllocateGC(w, w->core.depth,
2040                                  GCBackground | GCFont | GCStipple |
2041                                  GCFillStyle, &values, GCForeground,
2042                                  GCSubwindowMode | GCGraphicsExposures |
2043                                  GCDashOffset | GCDashList | GCArcMode);
2044         } else
2045 #endif
2046         {
2047                 tw->tabs.greyGC =
2048                     XtAllocateGC(w, w->core.depth,
2049                                  GCFont, &values,
2050                                  GCForeground,
2051                                  GCBackground | GCSubwindowMode |
2052                                  GCGraphicsExposures | GCDashOffset | GCDashList
2053                                  | GCArcMode);
2054         }
2055 }