diff lwlib/xlwtabs.c @ 398:74fd4e045ea6 r21-2-29

Import from CVS: tag r21-2-29
author cvs
date Mon, 13 Aug 2007 11:13:30 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/xlwtabs.c	Mon Aug 13 11:13:30 2007 +0200
@@ -0,0 +1,2101 @@
+ /* Tabs Widget for XEmacs.
+    Copyright (C) 1999 Edward A. Falk
+ 
+ This file is part of XEmacs.
+ 
+ XEmacs is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+ 
+ XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with XEmacs; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.  */
+ 
+ /* Synched up with: Tabs.c 1.27 */
+ 
+ /*
+ * Tabs.c - Index Tabs composite widget
+ *
+ * Author: Edward A. Falk
+ *	   falk@falconer.vip.best.com
+ *
+ * Date: July 29, 1997
+ *
+ *
+ * Overall layout of this widget is as follows:
+ *
+ *   ________ ,---------. _________
+ *  |  label ||  Label  ||  Label  |  \ tabs
+ *  |________||         ||_________|  /
+ *  |+----------------------------+|  \
+ *  ||                            ||  |
+ *  ||  child widget window       ||   > frame
+ *  |+----------------------------+|  |
+ *  +------------------------------+  /
+ *
+ * The height of the tabs includes the shadow width, top and bottom
+ * margins, and the height of the text.
+ *
+ * The height of the frame includes the top and bottom shadow width and the
+ * size of the child widget window.
+ *
+ * The tabs overlap the frame and each other vertically by the shadow
+ * width, so that when the topmost tab is drawn, it obliterates part of
+ * the frame.
+ */
+
+/*
+ * TODO: min child height = tab height
+ */
+
+#include	<config.h>
+#include	<stdio.h>
+
+#include	<X11/Xlib.h>
+#include	<X11/IntrinsicP.h>
+#include	<X11/StringDefs.h>
+#include	"../src/xmu.h"
+#include	"xlwtabsP.h"
+#include	"xlwgcs.h"
+
+#define	MIN_WID		10
+#define	MIN_HGT		10
+#define	INDENT		3	/* tabs indented from edge by this much */
+#define	SPACING		0	/* distance between tabs */
+#define	SHADWID		1	/* default shadow width */
+#define	TABDELTA	2	/* top tab grows this many pixels */
+#define	TABLDELTA	2	/* top tab label offset this many pixels */
+
+
+/****************************************************************
+ *
+ * IndexTabs Resources
+ *
+ ****************************************************************/
+
+static	char	defaultTranslations[] = "\
+	<BtnUp>:		select()	\n\
+	<FocusIn>:		highlight()	\n\
+	<FocusOut>:		unhighlight()	\n\
+	<Key>Page_Up:		page(up)	\n\
+	<Key>KP_Page_Up:	page(up)	\n\
+	<Key>Prior:		page(up)	\n\
+	<Key>KP_Prior:		page(up)	\n\
+	<Key>Page_Down:		page(down)	\n\
+	<Key>KP_Page_Down:	page(down)	\n\
+	<Key>Next:		page(down)	\n\
+	<Key>KP_Next:		page(down)	\n\
+	<Key>Home:		page(home)	\n\
+	<Key>KP_Home:		page(home)	\n\
+	<Key>End:		page(end)	\n\
+	<Key>KP_End:		page(end)	\n\
+	<Key>Up:		highlight(up)	\n\
+	<Key>KP_Up:		highlight(up)	\n\
+	<Key>Down:		highlight(down)	\n\
+	<Key>KP_Down:		highlight(down)	\n\
+	<Key> :			page(select)	\n\
+	 " ;
+
+static	char	accelTable[] = "	#augment\n\
+	<Key>Page_Up:		page(up)	\n\
+	<Key>KP_Page_Up:	page(up)	\n\
+	<Key>Prior:		page(up)	\n\
+	<Key>KP_Prior:		page(up)	\n\
+	<Key>Page_Down:		page(down)	\n\
+	<Key>KP_Page_Down:	page(down)	\n\
+	<Key>Next:		page(down)	\n\
+	<Key>KP_Next:		page(down)	\n\
+	<Key>Home:		page(home)	\n\
+	<Key>KP_Home:		page(home)	\n\
+	<Key>End:		page(end)	\n\
+	<Key>KP_End:		page(end)	\n\
+	<Key>Up:		highlight(up)	\n\
+	<Key>KP_Up:		highlight(up)	\n\
+	<Key>Down:		highlight(down)	\n\
+	<Key>KP_Down:		highlight(down)	\n\
+	<Key> :			page(select)	\n\
+	 " ;
+static	XtAccelerators	defaultAccelerators ;
+
+#define	offset(field)	XtOffsetOf(TabsRec, tabs.field)
+static XtResource resources[] = {
+
+  {XtNselectInsensitive, XtCSelectInsensitive, XtRBoolean, sizeof(Boolean),
+	offset(selectInsensitive), XtRImmediate, (XtPointer) True},
+  {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
+	offset(font), XtRString, (XtPointer) XtDefaultFont},
+  {XtNinternalWidth, XtCWidth, XtRDimension, sizeof(Dimension),
+	offset(internalWidth), XtRImmediate, (XtPointer)4 },
+  {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension),
+	offset(internalHeight), XtRImmediate, (XtPointer)4 },
+  {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
+	XtOffsetOf(RectObjRec,rectangle.border_width), XtRImmediate, (XtPointer)0},
+  {XtNtopWidget, XtCTopWidget, XtRWidget, sizeof(Widget),
+	offset(topWidget), XtRImmediate, NULL},
+  {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+	offset(callbacks), XtRCallback, NULL},
+  {XtNpopdownCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+	offset(popdownCallbacks), XtRCallback, NULL},
+  {XtNbeNiceToColormap, XtCBeNiceToColormap, XtRBoolean, sizeof(Boolean),
+	offset(be_nice_to_cmap), XtRImmediate, (XtPointer) True},
+  {XtNtopShadowContrast, XtCTopShadowContrast, XtRInt, sizeof(int),
+	offset(top_shadow_contrast), XtRImmediate, (XtPointer) 20},
+  {XtNbottomShadowContrast, XtCBottomShadowContrast, XtRInt, sizeof(int),
+	offset(bot_shadow_contrast), XtRImmediate, (XtPointer) 40},
+  {XtNinsensitiveContrast, XtCInsensitiveContrast, XtRInt, sizeof(int),
+	offset(insensitive_contrast), XtRImmediate, (XtPointer) 33},
+  {XtNaccelerators, XtCAccelerators, XtRAcceleratorTable,sizeof(XtTranslations),
+	XtOffsetOf(TabsRec,core.accelerators), XtRString, accelTable},
+};
+#undef	offset
+
+
+
+	/* constraint resources */
+
+#define	offset(field)	XtOffsetOf(TabsConstraintsRec, tabs.field)
+static XtResource tabsConstraintResources[] = {
+  {XtNtabLabel, XtCLabel, XtRString, sizeof(String),
+	offset(label), XtRString, NULL},
+  {XtNtabLeftBitmap, XtCLeftBitmap, XtRBitmap, sizeof(Pixmap),
+	offset(left_bitmap), XtRImmediate, None},
+  {XtNtabForeground, XtCForeground, XtRPixel, sizeof(Pixel),
+	offset(foreground), XtRString, (XtPointer) XtDefaultForeground},
+  {XtNresizable, XtCResizable, XtRBoolean, sizeof(Boolean),
+	offset(resizable), XtRImmediate, (XtPointer) True},
+} ;
+#undef	offset
+
+
+
+
+#if	!NeedFunctionPrototypes
+
+	/* FORWARD REFERENCES: */
+
+	/* member functions */
+
+static	void	TabsClassInit();
+static	void	TabsInit();
+static	void	TabsResize();
+static	void	TabsExpose();
+static	void	TabsDestroy();
+static	void	TabsRealize();
+static	Boolean	TabsSetValues();
+static	Boolean	TabsAcceptFocus();
+static	XtGeometryResult	TabsQueryGeometry();
+static	XtGeometryResult	TabsGeometryManager();
+static	void	TabsChangeManaged();
+static	void	TabsConstraintInitialize() ;
+static	Boolean	TabsConstraintSetValues() ;
+
+	/* action procs */
+
+static	void	TabsSelect() ;
+static	void	TabsPage() ;
+static	void	TabsHighlight() ;
+static	void	TabsUnhighlight() ;
+
+	/* internal privates */
+
+static	void	TabsAllocGCs() ;	/* get rendering GCs */
+static	void	TabsFreeGCs() ;		/* return rendering GCs */
+static	void	DrawTabs() ;		/* draw all tabs */
+static	void	DrawTab() ;		/* draw one index tab */
+static	void	DrawFrame() ;		/* draw frame around contents */
+static	void	DrawTrim() ;		/* draw trim around a tab */
+static	void	DrawBorder() ;		/* draw border */
+static	void	DrawHighlight() ;	/* draw highlight */
+static	void	UndrawTab() ;		/* undraw interior of a tab */
+static	void	TabWidth() ;		/* recompute tab size */
+static	void	GetPreferredSizes() ;	/* query all children for their sizes */
+static	void	MaxChild() ;		/* find max preferred child size */
+static	int	PreferredSize() ;	/* compute preferred size */
+static	int	PreferredSize2() ;	/* compute preferred size */
+static	int	PreferredSize3() ;	/* compute preferred size */
+static	void	MakeSizeRequest() ;	/* try to change size */
+static	void	getBitmapInfo() ;
+static	int	TabLayout() ;		/* lay out tabs */
+static	void	TabsShuffleRows() ;	/* bring current tab to bottom row */
+
+static	void	TabsAllocFgGC() ;
+static	void	TabsAllocGreyGC() ;
+
+#else
+
+static	void	TabsClassInit(void) ;
+static	void	TabsInit( Widget req, Widget new, ArgList, Cardinal *nargs) ;
+static	void	TabsConstraintInitialize(Widget, Widget, ArgList, Cardinal *) ;
+static	void	TabsRealize(Widget, Mask *, XSetWindowAttributes *) ;
+static	void	TabsDestroy( Widget w) ;
+static	void	TabsResize( Widget w) ;
+static	void	TabsExpose( Widget w, XEvent *event, Region region) ;
+static	Boolean	TabsSetValues(Widget, Widget, Widget, ArgList, Cardinal *) ;
+static	Boolean	TabsAcceptFocus(Widget, Time *);
+static	Boolean	TabsConstraintSetValues(Widget, Widget, Widget,
+			ArgList, Cardinal *) ;
+static	XtGeometryResult TabsQueryGeometry(Widget,
+				XtWidgetGeometry *, XtWidgetGeometry *) ;
+static	XtGeometryResult TabsGeometryManager(Widget,
+				XtWidgetGeometry *, XtWidgetGeometry *) ;
+static	void	TabsChangeManaged( Widget w) ;
+
+static	void	TabsSelect(Widget, XEvent *, String *, Cardinal *) ;
+static	void	TabsPage(Widget, XEvent *, String *, Cardinal *) ;
+static	void	TabsHighlight(Widget, XEvent *, String *, Cardinal *) ;
+static	void	TabsUnhighlight(Widget, XEvent *, String *, Cardinal *) ;
+
+static	void	DrawTabs( TabsWidget tw, Bool labels) ;
+static	void	DrawTab( TabsWidget tw, Widget child, Bool labels) ;
+static	void	DrawFrame( TabsWidget tw) ;
+static	void	DrawTrim( TabsWidget, int x, int y,
+		  int wid, int hgt, Bool bottom, Bool undraw) ;
+static	void	DrawBorder( TabsWidget tw, Widget child, Bool undraw) ;
+static	void	DrawHighlight( TabsWidget tw, Widget child, Bool undraw) ;
+static	void	UndrawTab( TabsWidget tw, Widget child) ;
+
+static	void	TabWidth( Widget w) ;
+static	int	TabLayout( TabsWidget, int wid, int hgt, Dimension *r_hgt,
+			Bool query_only) ;
+static	void	GetPreferredSizes(TabsWidget) ;
+static	void	MaxChild(TabsWidget, Widget except, Dimension, Dimension) ;
+static	void	TabsShuffleRows( TabsWidget tw) ;
+static	int	PreferredSize( TabsWidget,
+			Dimension *reply_width, Dimension *reply_height,
+			Dimension *reply_cw, Dimension *reply_ch) ;
+static	int	PreferredSize2( TabsWidget, int cw, int ch,
+			Dimension *rw, Dimension *rh) ;
+static	int	PreferredSize3( TabsWidget, int wid, int hgt,
+			Dimension *rw, Dimension *rh) ;
+static	void	MakeSizeRequest(TabsWidget) ;
+
+static	void	TabsAllocGCs(TabsWidget) ;
+static	void	TabsFreeGCs(TabsWidget) ;
+static	void	getBitmapInfo( TabsWidget tw, TabsConstraints tab) ;
+static	void	TabsAllocFgGC( TabsWidget tw) ;
+static	void	TabsAllocGreyGC( TabsWidget tw) ;
+
+#endif
+
+#define	AddRect(i,xx,yy,w,h)	\
+  do{rects[(i)].x=(xx); rects[i].y=(yy);	\
+     rects[i].width=(w); rects[i].height=(h);}while(0)
+
+static	XtActionsRec	actionsList[] =
+  {
+    {"select",	TabsSelect},
+    {"page",	TabsPage},
+    {"highlight", TabsHighlight},
+    {"unhighlight", TabsUnhighlight},
+  } ;
+
+
+/****************************************************************
+*
+* Full class record constant
+*
+****************************************************************/
+
+#ifndef	NEED_MOTIF
+#define	SuperClass	(&constraintClassRec)
+#else
+#define	SuperClass	(&xmManagerClassRec)
+#endif
+
+TabsClassRec tabsClassRec = {
+  {
+/* core_class fields      */
+    /* superclass         */    (WidgetClass) SuperClass,
+    /* class_name         */    "Tabs",
+    /* widget_size        */    sizeof(TabsRec),
+    /* class_initialize   */    TabsClassInit,
+    /* class_part_init    */	NULL,			/* TODO? */
+    /* class_inited       */	FALSE,
+    /* initialize         */    TabsInit,
+    /* initialize_hook    */	NULL,
+    /* realize            */    TabsRealize,
+    /* actions            */    actionsList,
+    /* num_actions	  */	XtNumber(actionsList),
+    /* resources          */    resources,
+    /* num_resources      */    XtNumber(resources),
+    /* xrm_class          */    NULLQUARK,
+    /* compress_motion	  */	TRUE,
+#if XtSpecificationRelease < 6
+    /* compress_exposure  */	XtExposeCompressMaximal,
+#else
+    /* compress_exposure  */	XtExposeCompressMaximal|XtExposeNoRegion,
+#endif
+    /* compress_enterleave*/	TRUE,
+    /* visible_interest   */    TRUE,
+    /* destroy            */    TabsDestroy,
+    /* resize             */    TabsResize,
+    /* expose             */    TabsExpose,
+    /* set_values         */    TabsSetValues,
+    /* set_values_hook    */	NULL,
+    /* set_values_almost  */    XtInheritSetValuesAlmost,
+    /* get_values_hook    */	NULL,
+    /* accept_focus       */    TabsAcceptFocus,
+    /* version            */	XtVersion,
+    /* callback_private   */    NULL,
+    /* tm_table           */    defaultTranslations,
+    /* query_geometry     */	TabsQueryGeometry,
+    /* display_accelerator*/	XtInheritDisplayAccelerator,
+    /* extension          */	NULL
+  },
+  {
+/* composite_class fields */
+    /* geometry_manager   */    TabsGeometryManager,
+    /* change_managed     */    TabsChangeManaged,
+    /* insert_child	  */	XtInheritInsertChild,	/* TODO? */
+    /* delete_child	  */	XtInheritDeleteChild,	/* TODO? */
+    /* extension          */	NULL
+  },
+  {
+/* constraint_class fields */
+    /* subresources	  */	tabsConstraintResources,
+    /* subresource_count  */	XtNumber(tabsConstraintResources),
+    /* constraint_size	  */	sizeof(TabsConstraintsRec),
+    /* initialize	  */	TabsConstraintInitialize,
+    /* destroy		  */	NULL,
+    /* set_values	  */	TabsConstraintSetValues,
+    /* extension	  */	NULL,
+  },
+#ifdef	NEED_MOTIF
+/* Manager Class fields */
+  {
+    /* translations		*/	NULL,
+    /* syn_resources		*/	NULL,
+    /* num_syn_resources	*/	0,
+    /* syn_constraint_resources	*/	NULL,
+    /* num_syn_constraint_resources */	0,
+    /* parent_process		*/	XmInheritParentProcess,
+    /* extension		*/	NULL
+  },
+#endif
+  {
+/* Tabs class fields */
+    /* extension	  */	NULL,
+  }
+};
+
+WidgetClass tabsWidgetClass = (WidgetClass)&tabsClassRec;
+
+
+
+#ifdef	DEBUG
+#ifdef	__STDC__
+#define	assert(e) \
+	  if(!(e)) fprintf(stderr,"yak! %s at %s:%d\n",#e,__FILE__,__LINE__)
+#else
+#define	assert(e) \
+	  if(!(e)) fprintf(stderr,"yak! e at %s:%d\n",__FILE__,__LINE__)
+#endif
+#else
+#define	assert(e)
+#endif
+
+
+
+
+/****************************************************************
+ *
+ * Member Procedures
+ *
+ ****************************************************************/
+
+static void
+TabsClassInit(void)
+{
+	defaultAccelerators = XtParseAcceleratorTable(accelTable) ;
+	/* TODO: register converter for labels? */
+}
+
+
+
+	/* Init a newly created tabs widget.  Compute height of tabs
+	 * and optionally compute size of widget. */
+
+/* ARGSUSED */
+
+static void
+TabsInit(Widget request, Widget new, ArgList args, Cardinal *num_args)
+{
+    TabsWidget newTw = (TabsWidget)new;
+
+    newTw->tabs.numRows = 0 ;
+    newTw->tabs.displayChildren = 0;
+
+    GetPreferredSizes(newTw) ;
+
+    /* height is easy, it's the same for all tabs:
+     *  TODO: font height + height of tallest bitmap.
+     */
+    newTw->tabs.tab_height = 2 * newTw->tabs.internalHeight + SHADWID ;
+
+    if( newTw->tabs.font != NULL )
+      newTw->tabs.tab_height += newTw->tabs.font->max_bounds.ascent +
+				newTw->tabs.font->max_bounds.descent ;
+
+    /* GC allocation is deferred until XtRealize() */
+
+    /* if size not explicitly set, set it to our preferred size now. */
+
+    if( request->core.width == 0 || request->core.height == 0 )
+    {
+      Dimension	w,h ;
+      PreferredSize(newTw, &w, &h, NULL,NULL) ;
+      if( request->core.width == 0 ) new->core.width = w ;
+      if( request->core.height == 0 ) new->core.height = h ;
+      XtClass(new)->core_class.resize(new) ;
+    }
+
+    /* defer GC allocation, etc., until Realize() time. */
+    newTw->tabs.foregroundGC =
+    newTw->tabs.backgroundGC =
+    newTw->tabs.greyGC =
+    newTw->tabs.topGC =
+    newTw->tabs.botGC = None ;
+
+    newTw->tabs.grey50 = None ;
+
+    newTw->tabs.needs_layout = False ;
+
+    newTw->tabs.hilight = NULL ;
+
+#ifdef	NEED_MOTIF
+    newTw->manager.navigation_type = XmTAB_GROUP ;
+    newTw->manager.traversal_on = True ;
+#endif
+}
+
+
+	/* Init the constraint part of a new tab child.  Compute the
+	 * size of the tab.
+	 */
+/* ARGSUSED */
+static	void
+TabsConstraintInitialize(Widget request, Widget new,
+	ArgList args, Cardinal *num_args)
+{
+	TabsConstraints tab = (TabsConstraints) new->core.constraints ;
+	tab->tabs.greyAlloc = False ;	/* defer allocation of pixel */
+
+	getBitmapInfo((TabsWidget)XtParent(new), tab) ;
+	TabWidth(new) ;
+}
+
+
+
+	/* Called when tabs widget first realized.  Create the window
+	 * and allocate the GCs
+	 */
+
+static	void
+TabsRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
+{
+	TabsWidget tw = (TabsWidget) w;
+
+	attributes->bit_gravity = NorthWestGravity;
+	*valueMask |= CWBitGravity;
+
+	SuperClass->core_class.realize(w, valueMask, attributes);
+
+	TabsAllocGCs(tw) ;
+}
+
+
+
+static	void
+TabsDestroy(Widget w)
+{
+	TabsFreeGCs((TabsWidget)w) ;
+}
+
+
+	/* Parent has resized us.  This will require that the tabs be
+	 * laid out again.
+	 */
+
+static void
+TabsResize(Widget w)
+{
+	TabsWidget	tw = (TabsWidget) w;
+	int		i ;
+	int		num_children = tw->composite.num_children ;
+	Widget		*childP ;
+	TabsConstraints tab ;
+	Dimension	cw,ch,bw ;
+
+	/* Our size has now been dictated by the parent.  Lay out the
+	 * tabs, lay out the frame, lay out the children.  Remember
+	 * that the tabs overlap each other and the frame by shadowWidth.
+	 * Also, the top tab is larger than the others, so if there's only
+	 * one row, the widget must be made taller to accommodate this.
+	 *
+	 * Once the tabs are laid out, if there is more than one
+	 * row, we may need to shuffle the rows to bring the top tab
+	 * to the bottom row.
+	 */
+
+	tw->tabs.needs_layout = False ;
+
+	if( num_children > 0 && tw->composite.children != NULL )
+	{
+	  /* Loop through the tabs and assign rows & x positions */
+	  (void) TabLayout(tw, tw->core.width, tw->core.height, NULL, False) ;
+	  num_children = tw->tabs.displayChildren;
+
+	  /* assign a top widget, bring it to bottom row. */
+	  TabsShuffleRows(tw) ;
+
+	  /* now assign child positions & sizes.  Positions are all the
+	   * same: just inside the frame.  Sizes are also all the same.
+	   */
+
+	  tw->tabs.child_width = cw = tw->core.width - 2 * SHADWID ;
+	  tw->tabs.child_height = ch =
+			tw->core.height - tw->tabs.tab_total - 2 * SHADWID ;
+
+
+	  for(i=0, childP=tw->composite.children;
+		i < num_children;
+		++i, ++childP)
+	    if( XtIsManaged(*childP) )
+	    {
+	      tab = (TabsConstraints) (*childP)->core.constraints ;
+	      bw = (*childP)->core.border_width ;
+	      XtConfigureWidget(*childP, SHADWID,tw->tabs.tab_total+SHADWID,
+			  cw-bw*2,ch-bw*2, bw) ;
+	    }
+	  if( XtIsRealized(w) ) {
+	    XClearWindow(XtDisplay((Widget)tw), XtWindow((Widget)tw)) ;
+	    /* should not be necessary to explicitly repaint after a
+	     * resize, but XEmacs folks tell me it is.
+	     */
+	    XtClass(tw)->core_class.expose((Widget)tw,NULL,None) ;
+	  }
+	}
+} /* Resize */
+
+
+
+	/* Redraw entire Tabs widget */
+
+/* ARGSUSED */
+static	void
+TabsExpose(Widget w, XEvent *event, Region region)
+{
+	TabsWidget	tw = (TabsWidget) w;
+
+	if( tw->tabs.needs_layout )
+	  XtClass(w)->core_class.resize(w) ;
+
+	DrawTabs(tw, True) ;
+}
+
+
+	/* Called when any Tabs widget resources are changed. */
+
+/* ARGSUSED */
+static	Boolean
+TabsSetValues(Widget current, Widget request, Widget new,
+	ArgList args, Cardinal *num_args)
+{
+	TabsWidget curtw = (TabsWidget) current ;
+	TabsWidget tw = (TabsWidget) new ;
+	Boolean	needRedraw = False ;
+	Widget	*childP ;
+	int	i ;
+
+
+	if( tw->tabs.font != curtw->tabs.font  ||
+	    tw->tabs.internalWidth != curtw->tabs.internalWidth ||
+	    tw->tabs.internalHeight != curtw->tabs.internalHeight )
+	{
+	  tw->tabs.tab_height = 2 * tw->tabs.internalHeight + SHADWID ;
+
+	  if( tw->tabs.font != NULL )
+	    tw->tabs.tab_height += tw->tabs.font->max_bounds.ascent +
+				   tw->tabs.font->max_bounds.descent ;
+
+	  /* Tab size has changed.  Resize all tabs and request a new size */
+	  for(i=0, childP=tw->composite.children;
+		i < tw->composite.num_children;
+		++i, ++childP)
+	    if( XtIsManaged(*childP) )
+	      TabWidth(*childP) ;
+	  PreferredSize(tw, &tw->core.width, &tw->core.height, NULL,NULL) ;
+	  needRedraw = True ;
+	  tw->tabs.needs_layout = True ;
+	}
+
+	/* TODO: if any color changes, need to recompute GCs and redraw */
+
+	if( tw->core.background_pixel != curtw->core.background_pixel ||
+	    tw->core.background_pixmap != curtw->core.background_pixmap ||
+	    tw->tabs.font != curtw->tabs.font )
+	  if( XtIsRealized(new) )
+	  {
+	    TabsFreeGCs(tw) ;
+	    TabsAllocGCs(tw) ;
+	    needRedraw = True ;
+	  }
+
+	if( tw->core.sensitive != curtw->core.sensitive )
+	  needRedraw = True ;
+
+	/* If top widget changes, need to change stacking order, redraw tabs.
+	 * Window system will handle the redraws.
+	 */
+
+	if( tw->tabs.topWidget != curtw->tabs.topWidget ) 
+	{
+	  if( XtIsRealized(tw->tabs.topWidget) )
+	  {
+	    Widget		w = tw->tabs.topWidget ;
+	    TabsConstraints	tab = (TabsConstraints) w->core.constraints ;
+
+	    XRaiseWindow(XtDisplay(w), XtWindow(w)) ;
+#ifdef	NEED_MOTIF
+	    XtVaSetValues(curtw->tabs.topWidget, XmNtraversalOn, False, 0) ;
+	    XtVaSetValues(w, XmNtraversalOn, True, 0) ;
+#endif
+
+	    if( tab->tabs.row != tw->tabs.numRows-1 )
+	      TabsShuffleRows(tw) ;
+
+	    needRedraw = True ;
+	  }
+	  else
+	    tw->tabs.needs_layout = True ;
+	}
+
+	return needRedraw ;
+}
+
+
+	/* Called when any child constraint resources change. */
+
+/* ARGSUSED */
+static	Boolean
+TabsConstraintSetValues(Widget current, Widget request, Widget new,
+	ArgList args, Cardinal *num_args)
+{
+	TabsWidget tw = (TabsWidget) XtParent(new) ;
+	TabsConstraints ctab = (TabsConstraints) current->core.constraints ;
+	TabsConstraints tab = (TabsConstraints) new->core.constraints ;
+
+
+	/* if label changes, need to re-layout the entire widget */
+	/* if foreground changes, need to redraw tab label */
+
+	/* TODO: only need resize of new bitmap has different dimensions
+	 * from old bitmap.
+	 */
+
+	if( tab->tabs.label != ctab->tabs.label ||  /* Tab size has changed. */
+	    tab->tabs.left_bitmap != ctab->tabs.left_bitmap )
+	{
+	  TabWidth(new) ;
+	  tw->tabs.needs_layout = True ;
+
+	  if( tab->tabs.left_bitmap != ctab->tabs.left_bitmap )
+	    getBitmapInfo(tw, tab) ;
+
+	  /* If there are no subclass ConstraintSetValues procedures remaining
+	   * to be invoked, and if the preferred size has changed, ask
+	   * for a resize.
+	   */
+	  if( XtClass((Widget)tw) == tabsWidgetClass )
+	    MakeSizeRequest(tw) ;
+	}
+
+
+	/* The child widget itself never needs a redisplay, but the parent
+	 * Tabs widget might.
+	 */
+
+	if( XtIsRealized(new) )
+	{
+	  if( tw->tabs.needs_layout ) {
+	    XClearWindow(XtDisplay((Widget)tw), XtWindow((Widget)tw)) ;
+	    XtClass(tw)->core_class.expose((Widget)tw,NULL,None) ;
+	  }
+
+	  else if( tab->tabs.foreground != ctab->tabs.foreground )
+	    DrawTab(tw, new, True) ;
+	}
+
+	return False ;
+}
+
+
+static	Boolean
+TabsAcceptFocus(Widget w, Time *t)
+{
+	if( !w->core.being_destroyed && XtIsRealized(w) &&
+	    XtIsSensitive(w) && XtIsManaged(w) && w->core.visible )
+	{
+	  Widget p ;
+	  for(p = XtParent(w); !XtIsShell(p); p = XtParent(p)) ;
+	  XtSetKeyboardFocus(p,w) ;
+	  return True ;
+	}
+	else
+	  return False ;
+}
+
+
+
+/*
+ * Return preferred size.  Happily accept anything >= our preferred size.
+ * (TODO: is that the right thing to do?  Should we always return "almost"
+ * if offerred more than we need?)
+ */
+
+static XtGeometryResult
+TabsQueryGeometry(Widget w,
+	XtWidgetGeometry *intended, XtWidgetGeometry *preferred)
+{
+    register TabsWidget tw = (TabsWidget)w ;
+    XtGeometryMask mode = intended->request_mode ;
+
+    preferred->request_mode = CWWidth | CWHeight ;
+    PreferredSize(tw, &preferred->width, &preferred->height, NULL,NULL) ;
+
+    if( (!(mode & CWWidth) || intended->width == w->core.width)  &&
+        (!(mode & CWHeight) || intended->height == w->core.height) )
+      return XtGeometryNo ;
+
+    if( (!(mode & CWWidth) || intended->width >= preferred->width)  &&
+	(!(mode & CWHeight) || intended->height >= preferred->height) )
+      return XtGeometryYes;
+
+    return XtGeometryAlmost;
+}
+
+
+
+/*
+ * Geometry Manager; called when a child wants to be resized.
+ */
+
+static XtGeometryResult
+TabsGeometryManager(Widget w, XtWidgetGeometry *req, XtWidgetGeometry *reply)
+{
+	TabsWidget		tw = (TabsWidget) XtParent(w);
+	Dimension		s = SHADWID ;
+	TabsConstraints		tab = (TabsConstraints)w->core.constraints;
+	XtGeometryResult	result ;
+	Dimension		rw, rh ;
+
+	/* Position request always denied */
+
+	if( ((req->request_mode & CWX) && req->x != w->core.x) ||
+	    ((req->request_mode & CWY) && req->y != w->core.y) ||
+	    !tab->tabs.resizable )
+	  return XtGeometryNo ;
+
+	/* Make all three fields in the request valid */
+	if( !(req->request_mode & CWWidth) )
+	    req->width = w->core.width;
+	if( !(req->request_mode & CWHeight) )
+	    req->height = w->core.height;
+	if( !(req->request_mode & CWBorderWidth) )
+	    req->border_width = w->core.border_width;
+
+	if( req->width == w->core.width &&
+	    req->height == w->core.height &&
+	    req->border_width == w->core.border_width )
+	  return XtGeometryNo ;
+
+	rw = req->width + 2 * req->border_width ;
+	rh = req->height + 2 * req->border_width ;
+
+	/* find out how big the children want to be now */
+	MaxChild(tw, w, rw, rh) ;
+
+
+	/* Size changes must see if the new size can be accommodated.
+	 * The Tabs widget keeps all of its children the same
+	 * size.  A request to shrink will be accepted only if the
+	 * new size is still big enough for all other children.  A
+	 * request to shrink that is not big enough for all children
+	 * returns an "almost" response with the new proposed size
+	 * or a "no" response if unable to shrink at all.
+	 *
+	 * A request to grow will be accepted only if the Tabs parent can
+	 * grow to accommodate.
+	 *
+	 * TODO:
+	 * We could get fancy here and re-arrange the tabs if it is
+	 * necessary to compromise with the parent, but we'll save that
+	 * for another day.
+	 */
+
+	if (req->request_mode & (CWWidth | CWHeight | CWBorderWidth))
+	{
+	  Dimension	cw,ch ;		/* children's preferred size */
+	  Dimension	aw,ah ;		/* available size we can give child */
+	  Dimension	th ;		/* space used by tabs */
+	  Dimension	wid,hgt ;	/* Tabs widget size */
+
+	  cw = tw->tabs.max_cw ;
+	  ch = tw->tabs.max_ch ;
+
+	  /* find out what *my* resulting preferred size would be */
+
+	  PreferredSize2(tw, cw, ch, &wid, &hgt) ;
+
+	  /* Would my size change?  If so, ask to be resized. */
+
+	  if( wid != tw->core.width || hgt != tw->core.height )
+	  {
+	    Dimension	oldWid = tw->core.width, oldHgt = tw->core.height ;
+	    XtWidgetGeometry	myrequest, myreply ;
+
+	    myrequest.width = wid ;
+	    myrequest.height = hgt ;
+	    myrequest.request_mode = CWWidth | CWHeight ;
+
+	    /* If child is only querying, or if we're going to have to
+	     * offer the child a compromise, then make this a query only.
+	     */
+
+	    if( (req->request_mode & XtCWQueryOnly)  || rw < cw || rh < ch )
+	      myrequest.request_mode |= XtCWQueryOnly ;
+
+	    result = XtMakeGeometryRequest((Widget)tw, &myrequest, &myreply) ;
+
+	    /* !$@# Athena Box widget changes the core size even if QueryOnly
+	     * is set.  I'm convinced this is a bug.  At any rate, to work
+	     * around the bug, we need to restore the core size after every
+	     * query geometry request.  This is only partly effective,
+	     * as there may be other boxes further up the tree.
+	     */
+	    if( myrequest.request_mode & XtCWQueryOnly ) {
+	      tw->core.width = oldWid ;
+	      tw->core.height = oldHgt ;
+	    }
+
+	    /* based on the parent's response, determine what the
+	     * resulting Tabs widget size would be.
+	     */
+
+	    switch( result ) {
+	      case XtGeometryYes:
+	      case XtGeometryDone:
+		tw->tabs.needs_layout = True ;
+		break ;
+
+	      case XtGeometryNo:
+		wid = tw->core.width ;
+		hgt = tw->core.height ;
+		break ;
+
+	      case XtGeometryAlmost:
+		wid = myreply.width ;
+		hgt = myreply.height ;
+		tw->tabs.needs_layout = True ;
+		break ;
+	    }
+	  }
+
+	  /* Within the constraints imposed by the parent, what is
+	   * the max size we can give the child?
+	   */
+	  (void) TabLayout(tw, wid, hgt, &th, True) ;
+	  aw = wid - 2*s ;
+	  ah = hgt - th - 2*s ;
+
+	  /* OK, make our decision.  If requested size is >= max sibling
+	   * preferred size, AND requested size <= available size, then
+	   * we accept.  Otherwise, we offer a compromise.
+	   */
+
+	  if( rw == aw && rh == ah )
+	  {
+	    /* Acceptable.  If this wasn't a query, change *all* children
+	     * to this size.
+	     */
+	    if( req->request_mode & XtCWQueryOnly )
+	      return XtGeometryYes ;
+	    else
+	    {
+	      Widget	*childP = tw->composite.children ;
+	      int	i,bw ;
+	      w->core.border_width = req->border_width ;
+	      for(i=tw->tabs.displayChildren; --i >= 0; ++childP)
+		if( XtIsManaged(*childP) )
+		{
+		  bw = (*childP)->core.border_width ;
+		  XtConfigureWidget(*childP, s,tw->tabs.tab_total+s,
+			    rw-2*bw, rh-2*bw, bw) ;
+		}
+#ifdef	COMMENT
+	      /* TODO: under what conditions will we need to redraw? */
+	      XClearWindow(XtDisplay((Widget)tw), XtWindow((Widget)tw)) ;
+	      XtClass(tw)->core_class.expose((Widget)tw,NULL,NULL) ;
+#endif	/* COMMENT */
+	      return XtGeometryDone ;
+	    }
+	  }
+
+	  /* Cannot grant child's request.  Describe what we *can* do
+	   * and return counter-offer.
+	   */
+	  reply->width  = aw - 2 * req->border_width ;
+	  reply->height = ah - 2 * req->border_width ;
+	  reply->border_width = req->border_width ;
+	  reply->request_mode = CWWidth | CWHeight | CWBorderWidth ;
+	  return XtGeometryAlmost ;
+	}
+
+	return XtGeometryYes ;
+}
+
+
+
+
+	/* The number of children we manage has changed; recompute
+	 * size from scratch.
+	 */
+
+static	void
+TabsChangeManaged(Widget w)
+{
+    TabsWidget	tw = (TabsWidget)w ;
+    Widget	*childP = tw->composite.children ;
+    int		i ;
+
+    if( tw->tabs.topWidget != NULL &&
+        ( !XtIsManaged(tw->tabs.topWidget) ||
+	  tw->tabs.topWidget->core.being_destroyed ) )
+      tw->tabs.topWidget = NULL ;
+
+    /* Check whether the highlight tab is still valid. */
+    if( tw->tabs.hilight != NULL &&
+        ( !XtIsManaged(tw->tabs.hilight) ||
+	  tw->tabs.hilight->core.being_destroyed ) )
+      tw->tabs.hilight = NULL ;
+
+    GetPreferredSizes(tw) ;
+    MakeSizeRequest(tw) ;
+
+    XtClass(w)->core_class.resize(w) ;
+    if( XtIsRealized(w) )
+    {
+      Display *dpy = XtDisplay(w) ;
+      XClearWindow(dpy, XtWindow(w)) ;
+      XtClass(w)->core_class.expose(w,NULL,NULL) ;
+
+      /* make sure the top widget stays on top.  This requires
+       * making sure that all new children are realized first.
+       */
+      if( tw->tabs.topWidget != NULL && XtIsRealized(tw->tabs.topWidget) )
+      {
+	for(i=tw->tabs.displayChildren; --i >= 0; ++childP)
+	  if( !XtIsRealized(*childP) )
+	    XtRealizeWidget(*childP) ;
+
+	XRaiseWindow(dpy, XtWindow(tw->tabs.topWidget)) ;
+      }
+    }
+
+#ifdef	NEED_MOTIF
+    /* Only top widget may receive input */
+
+    for(childP = tw->composite.children, i=tw->composite.num_children;
+        --i >= 0;
+	++childP)
+    {
+      XtVaSetValues(*childP, XmNtraversalOn, False, 0) ;
+    }
+
+    if( tw->tabs.topWidget != NULL )
+      XtVaSetValues(tw->tabs.topWidget, XmNtraversalOn, True, 0) ;
+#endif
+}
+
+
+
+
+/****************************************************************
+ *
+ * Action Procedures
+ *
+ ****************************************************************/
+
+
+	/* User clicks on a tab, figure out which one it was. */
+
+/* ARGSUSED */
+static	void
+TabsSelect(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+	TabsWidget	tw = (TabsWidget) w ;
+	Widget	*childP ;
+	Position x,y ;
+	Dimension h = tw->tabs.tab_height ;
+	int	i ;
+
+#ifdef	NEED_MOTIF
+	XmProcessTraversal (w, XmTRAVERSE_CURRENT) ;
+#endif
+
+	/* TODO: is there an Xmu function or something to do this instead? */
+	switch( event->type ) {
+	  case ButtonPress:
+	  case ButtonRelease:
+	    x = event->xbutton.x ; y = event->xbutton.y ; break ;
+	  case KeyPress:
+	  case KeyRelease:
+	    x = event->xkey.x ; y = event->xkey.y ; break ;
+	  default:
+	    return ;
+	}
+
+	/* TODO: determine which tab was clicked, if any.  Set that
+	 * widget to be top of stacking order with XawTabsSetTop().
+	 */
+	for(i=0, childP=tw->composite.children;
+	      i < tw->tabs.displayChildren;
+	      ++i, ++childP)
+	  if( XtIsManaged(*childP) )
+	  {
+	    TabsConstraints tab = (TabsConstraints)(*childP)->core.constraints;
+	    if( x > tab->tabs.x  &&  x < tab->tabs.x + tab->tabs.width  &&
+		y > tab->tabs.y  &&  y < tab->tabs.y + h )
+	    {
+	      if( *childP != tw->tabs.topWidget &&
+		  (XtIsSensitive(*childP) || tw->tabs.selectInsensitive) )
+		XawTabsSetTop(*childP, True) ;
+	      break ;
+	    }
+	  }
+}
+
+
+	/* User hits a key */
+
+static	void
+TabsPage(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+	TabsWidget	tw = (TabsWidget) w ;
+	Widget		newtop = NULL;
+	Widget		*childP ;
+	int		idx ;
+	int		nc = tw->tabs.displayChildren ;
+
+	if( nc <= 0 )
+	  return ;
+
+	if( *num_params < 1 ) {
+	  XtAppWarning(XtWidgetToApplicationContext(w),
+	    "Tabs: page() action called with no arguments") ;
+	  return ;
+	}
+
+	if( tw->tabs.topWidget == NULL )
+	  tw->tabs.topWidget = tw->composite.children[0] ;
+
+	for(idx=0, childP=tw->composite.children; idx < nc; ++idx, ++childP )
+	  if( tw->tabs.topWidget == *childP )
+	    break ;
+
+	switch( params[0][0] ) {
+	  case 'u':		/* up */
+	  case 'U':
+	    if( --idx < 0 )
+	      idx = nc-1 ;
+	    newtop = tw->composite.children[idx] ;
+	    break ;
+
+	  case 'd':		/* down */
+	  case 'D':
+	    if( ++idx >= nc )
+	      idx = 0 ;
+	    newtop = tw->composite.children[idx] ;
+	    break ;
+
+	  case 'h':
+	  case 'H':
+	  default:
+	      newtop = tw->composite.children[0] ;
+	      break ;
+
+	  case 'e':
+	  case 'E':
+	      newtop = tw->composite.children[nc-1] ;
+	      break ;
+
+	  case 's':		/* selected */
+	  case 'S':
+	      if( (newtop = tw->tabs.hilight) == NULL )
+		return ;
+	      break ;
+	}
+
+	XawTabsSetTop(newtop, True) ;
+}
+
+
+	/* User hits up/down key */
+
+static	void
+TabsHighlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+	TabsWidget	tw = (TabsWidget) w ;
+	Widget		newhl = NULL;
+	Widget		*childP ;
+	int		idx ;
+	int		nc = tw->tabs.displayChildren ;
+
+	if( nc <= 0 )
+	  return ;
+
+	if( *num_params < 1 )
+	{
+	  if( tw->tabs.hilight != NULL )
+	    DrawHighlight(tw, tw->tabs.hilight, False) ;
+	  return ;
+	}
+
+	if( tw->tabs.hilight == NULL )
+	  newhl = tw->composite.children[0] ;
+
+	else
+	{
+	  /* find index of currently highlit child */
+	  for(idx=0, childP=tw->composite.children; idx < nc; ++idx, ++childP )
+	    if( tw->tabs.hilight == *childP )
+	      break ;
+
+	  switch( params[0][0] ) {
+	    case 'u':		/* up */
+	    case 'U':
+	      if( --idx < 0 )
+		idx = nc-1 ;
+	      newhl = tw->composite.children[idx] ;
+	      break ;
+
+	    case 'd':		/* down */
+	    case 'D':
+	      if( ++idx >= nc )
+		idx = 0 ;
+	      newhl = tw->composite.children[idx] ;
+	      break ;
+
+	    case 'h':
+	    case 'H':
+		newhl = tw->composite.children[0] ;
+		break ;
+
+	    case 'e':
+	    case 'E':
+		newhl = tw->composite.children[nc-1] ;
+		break ;
+
+	    default:
+		newhl = tw->tabs.hilight ;
+		break ;
+	  }
+	}
+
+	XawTabsSetHighlight(w, newhl) ;
+}
+
+
+
+static	void
+TabsUnhighlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+	TabsWidget	tw = (TabsWidget) w ;
+	int		nc = tw->composite.num_children ;
+
+	if( nc <= 0 )
+	  return ;
+
+	if( tw->tabs.hilight != NULL )
+	  DrawHighlight(tw, tw->tabs.hilight, True) ;
+}
+
+
+
+
+
+/****************************************************************
+ *
+ * Public Procedures
+ *
+ ****************************************************************/
+
+
+	/* Set the top tab, optionally call all callbacks. */
+void
+XawTabsSetTop(Widget w, Bool callCallbacks)
+{
+	TabsWidget	tw = (TabsWidget)w->core.parent ;
+	TabsConstraints tab ;
+	Widget		oldtop = tw->tabs.topWidget ;
+
+	if( !XtIsSubclass(w->core.parent, tabsWidgetClass) )
+	{
+	  char line[256] ;
+	  sprintf(line, "XawTabsSetTop: widget \"%.64s\" is not the child of a tabs widget.", XtName(w)) ;
+	  XtAppWarning(XtWidgetToApplicationContext(w), line) ;
+	  return ;
+	}
+
+	if( callCallbacks )
+	  XtCallCallbackList(w, tw->tabs.popdownCallbacks,
+		(XtPointer)tw->tabs.topWidget) ;
+
+	if( !XtIsRealized(w) ) {
+	  tw->tabs.topWidget = w ;
+	  tw->tabs.needs_layout = True ;
+	  tw->tabs.hilight = NULL; /* The highlight tab might disappear. */
+	  return ;
+	}
+
+	XRaiseWindow(XtDisplay(w), XtWindow(w)) ;
+#ifdef	NEED_MOTIF
+	XtVaSetValues(oldtop, XmNtraversalOn, False, 0) ;
+	XtVaSetValues(w, XmNtraversalOn, True, 0) ;
+#endif
+
+	tab = (TabsConstraints) w->core.constraints ;
+
+	/* Unhighlight before we start messing with the stacking order. */
+	if( tw->tabs.hilight != NULL )
+	  {
+	    DrawHighlight(tw, tw->tabs.hilight, True) ;
+	    tw->tabs.hilight = NULL;
+	  }
+
+	if( tab->tabs.row == 0 )
+	{
+	  /* Easy case; undraw current top, undraw new top, assign new
+	   * top, redraw all borders.
+	   * We *could* just erase and execute a full redraw, but I like to
+	   * reduce screen flicker.
+	   */
+	  UndrawTab(tw, oldtop) ;		/* undraw old */
+	  DrawBorder(tw, oldtop, True) ;
+	  UndrawTab(tw, w) ;			/* undraw new */
+	  DrawBorder(tw, w, True) ;
+	  tw->tabs.topWidget = w ;
+	  DrawTab(tw, oldtop, True) ;		/* redraw old */
+	  DrawTab(tw, w, True) ;		/* redraw new */
+	  DrawTabs(tw, False) ;
+	}
+	else
+	{
+	  tw->tabs.topWidget = w ;
+	  TabsShuffleRows(tw) ;
+	  XClearWindow(XtDisplay((Widget)tw), XtWindow((Widget)tw)) ;
+	  XtClass(tw)->core_class.expose((Widget)tw,NULL,None) ;
+	}
+
+	XawTabsSetHighlight((Widget)tw, w) ;
+
+	if( callCallbacks )
+	  XtCallCallbackList(w, tw->tabs.callbacks, (XtPointer)w) ;
+}
+
+
+	/* Set the top tab, optionally call all callbacks. */
+void
+XawTabsSetHighlight(Widget t, Widget w)
+{
+	TabsWidget	tw = (TabsWidget)t ;
+
+	if( !XtIsSubclass(t, tabsWidgetClass) )
+	  return ;
+
+	if( XtIsRealized(t) && w != tw->tabs.hilight )
+	{
+	  if( w != NULL )
+	    DrawHighlight(tw, w, False) ;
+	}
+
+	tw->tabs.hilight = w ;
+}
+
+
+
+
+/****************************************************************
+ *
+ * Private Procedures
+ *
+ ****************************************************************/
+
+
+static	void
+TabsAllocGCs(TabsWidget tw)
+{
+	TabsAllocFgGC(tw) ;
+	TabsAllocGreyGC(tw) ;
+	tw->tabs.backgroundGC = AllocBackgroundGC((Widget)tw, None) ;
+	tw->tabs.topGC = AllocTopShadowGC((Widget)tw,
+		tw->tabs.top_shadow_contrast, tw->tabs.be_nice_to_cmap) ;
+	tw->tabs.botGC = AllocBotShadowGC((Widget)tw,
+		tw->tabs.bot_shadow_contrast, tw->tabs.be_nice_to_cmap) ;
+}
+
+
+static	void
+TabsFreeGCs(TabsWidget tw)
+{
+	Widget w = (Widget) tw;
+
+	XtReleaseGC(w, tw->tabs.foregroundGC) ;
+	XtReleaseGC(w, tw->tabs.greyGC) ;
+	XtReleaseGC(w, tw->tabs.backgroundGC) ;
+	XtReleaseGC(w, tw->tabs.topGC) ;
+	XtReleaseGC(w, tw->tabs.botGC) ;
+#ifdef HAVE_XMU
+	XmuReleaseStippledPixmap(XtScreen(w), tw->tabs.grey50) ;
+#endif
+}
+
+
+
+
+
+	/* Redraw entire Tabs widget */
+
+static	void
+DrawTabs(TabsWidget tw, Bool labels)
+{
+	Widget		*childP ;
+	int		i,j ;
+	Dimension	s = SHADWID ;
+	Dimension	th = tw->tabs.tab_height ;
+	Position	y ;
+	TabsConstraints	tab ;
+
+ 	if( !XtIsRealized((Widget)tw))
+ 	  return ;
+ 
+	/* draw tabs and frames by row except for the top tab, which
+	 * is drawn last.  (This is inefficiently written, but should not
+	 * be too slow as long as there are not a lot of rows.)
+	 */
+
+	y = tw->tabs.numRows == 1 ? TABDELTA : 0 ;
+	for(i=0; i<tw->tabs.numRows; ++i, y += th)
+	{
+ 	  for( j=tw->tabs.displayChildren, childP=tw->composite.children;
+  	      --j >= 0; ++childP )
+	    if( XtIsManaged(*childP) )
+	    {
+	      tab = (TabsConstraints)(*childP)->core.constraints;
+	      if( tab->tabs.row == i && *childP != tw->tabs.topWidget )
+		DrawTab(tw, *childP, labels) ;
+	    }
+	  if( i != tw->tabs.numRows -1 )
+	    DrawTrim(tw, 0,y+th, tw->core.width, th+s, False,False) ;
+	}
+
+	DrawFrame(tw) ;
+
+	/* and now the top tab */
+	if( tw->tabs.topWidget != NULL )
+	  DrawTab(tw, tw->tabs.topWidget, labels) ;
+}
+
+
+
+/* Draw one tab.  Corners are rounded very slightly. */
+
+static	void
+DrawTab(TabsWidget tw, Widget child, Bool labels)
+{
+	GC	gc ;
+	int	x,y ;
+
+ 	if( !XtIsRealized((Widget)tw))
+ 	  return ;
+
+	DrawBorder(tw, child, False) ;
+
+	if( labels )
+	{
+	  TabsConstraints tab = (TabsConstraints)child->core.constraints;
+	  Display	*dpy = XtDisplay((Widget)tw) ;
+	  Window	win = XtWindow((Widget)tw) ;
+	  String	lbl = tab->tabs.label != NULL ?
+			      tab->tabs.label : XtName(child) ;
+
+	  if( XtIsSensitive(child) )
+	  {
+	    gc = tw->tabs.foregroundGC ;
+	    XSetForeground(dpy, gc, tab->tabs.foreground) ;
+	  }
+	  else
+	  {
+	    /* grey pixel allocation deferred until now */
+	    if( !tab->tabs.greyAlloc )
+	    {
+	      if( tw->tabs.be_nice_to_cmap || tw->core.depth == 1 )
+		tab->tabs.grey = tab->tabs.foreground ;
+	      else
+		tab->tabs.grey = AllocGreyPixel((Widget)tw,
+					tab->tabs.foreground,
+					tw->core.background_pixel,
+					tw->tabs.insensitive_contrast ) ;
+	      tab->tabs.greyAlloc = True ;
+	    }
+	    gc = tw->tabs.greyGC ;
+	    XSetForeground(dpy, gc, tab->tabs.grey) ;
+	  }
+
+	  x = tab->tabs.x ;
+	  y = tab->tabs.y ;
+	  if( child == tw->tabs.topWidget )
+	    y -= TABLDELTA ;
+
+	  if( tab->tabs.left_bitmap != None && tab->tabs.lbm_width > 0 )
+	  {
+	    if( tab->tabs.lbm_depth == 1 )
+	      XCopyPlane(dpy, tab->tabs.left_bitmap, win,gc,
+		0,0, tab->tabs.lbm_width, tab->tabs.lbm_height,
+		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y, 1L) ;
+	    else
+	      XCopyArea(dpy, tab->tabs.left_bitmap, win,gc,
+		0,0, tab->tabs.lbm_width, tab->tabs.lbm_height,
+		x+tab->tabs.lbm_x, y+tab->tabs.lbm_y) ;
+	  }
+
+	  if( lbl != NULL && tw->tabs.font != NULL )
+	    XDrawString(dpy,win,gc,
+	      x+tab->tabs.l_x, y+tab->tabs.l_y,
+	      lbl, (int)strlen(lbl)) ;
+	}
+
+	if( child == tw->tabs.hilight )
+	  DrawHighlight(tw, child, False) ;
+}
+
+
+	/* draw frame all the way around the child windows. */
+
+static	void
+DrawFrame(TabsWidget tw)
+{
+	GC		topgc = tw->tabs.topGC ;
+	GC		botgc = tw->tabs.botGC ;
+	Dimension	s = SHADWID ;
+	Dimension	ch = tw->tabs.child_height ;
+	Draw3dBox((Widget)tw, 0,tw->tabs.tab_total,
+		tw->core.width, ch+2*s, s, topgc, botgc) ;
+}
+
+
+	/* draw trim around a tab or underneath a row of tabs */
+
+static	void
+DrawTrim(TabsWidget tw,		/* widget */
+	int	x,		/* upper-left corner */
+	int	y,
+	int	wid,		/* total size */
+	int	hgt,
+	Bool	bottom,		/* draw bottom? */
+	Bool	undraw)		/* undraw all */
+{
+	Display		*dpy = XtDisplay((Widget)tw) ;
+	Window		win = XtWindow((Widget)tw) ;
+	GC		bggc = tw->tabs.backgroundGC ;
+	GC		topgc = undraw ? bggc : tw->tabs.topGC ;
+	GC		botgc = undraw ? bggc : tw->tabs.botGC ;
+	if( bottom )
+	  XDrawLine(dpy,win,bggc, x,y+hgt-1, x+wid-1,y+hgt-1) ;	/* bottom */
+	XDrawLine(dpy,win,topgc, x,y+2, x,y+hgt-2) ;		/* left */
+	XDrawPoint(dpy,win,topgc, x+1,y+1) ;			/* corner */
+	XDrawLine(dpy,win,topgc, x+2,y, x+wid-3,y) ;		/* top */
+	XDrawLine(dpy,win,botgc, x+wid-2,y+1, x+wid-2,y+hgt-2) ; /* right */
+	XDrawLine(dpy,win,botgc, x+wid-1,y+2, x+wid-1,y+hgt-2) ; /* right */
+}
+
+
+/* Draw one tab border. */
+
+static	void
+DrawBorder(TabsWidget tw, Widget child, Bool undraw)
+{
+	TabsConstraints tab = (TabsConstraints)child->core.constraints;
+	Position	x = tab->tabs.x ;
+	Position	y = tab->tabs.y ;
+	Dimension	twid = tab->tabs.width ;
+	Dimension	thgt = tw->tabs.tab_height ;
+
+	/* top tab requires a little special attention; it overlaps
+	 * neighboring tabs slightly, so the background must be cleared
+	 * in the region of the overlap to partially erase those neighbors.
+	 * TODO: is this worth doing with regions instead?
+	 */
+	if( child == tw->tabs.topWidget )
+	{
+	  Display	*dpy = XtDisplay((Widget)tw) ;
+	  Window	win = XtWindow((Widget)tw) ;
+	  GC		bggc = tw->tabs.backgroundGC ;
+	  XRectangle	rects[3] ;
+	  x -= TABDELTA ;
+	  y -= TABDELTA ;
+	  twid += TABDELTA*2 ;
+	  thgt += TABDELTA ;
+	  AddRect(0, x,y+1,twid,TABDELTA) ;
+	  AddRect(1, x+1,y,TABDELTA,thgt) ;
+	  AddRect(2, x+twid-TABDELTA-1,y,TABDELTA,thgt) ;
+	  XFillRectangles(dpy,win,bggc, rects, 3) ;
+	}
+
+	DrawTrim(tw, x,y,twid,thgt+1, child == tw->tabs.topWidget, undraw) ;
+}
+
+
+/* Draw highlight around tab that has focus */
+
+static	void
+DrawHighlight(TabsWidget tw, Widget child, Bool undraw)
+{
+	TabsConstraints tab = (TabsConstraints)child->core.constraints;
+	Display		*dpy = XtDisplay((Widget)tw) ;
+	Window		win = XtWindow((Widget)tw) ;
+	GC		gc ;
+	Position	x = tab->tabs.x ;
+	Position	y = tab->tabs.y ;
+	Dimension	wid = tab->tabs.width ;
+	Dimension	hgt = tw->tabs.tab_height ;
+	XPoint		points[6] ;
+
+	/* top tab does not have a highlight */
+
+	if( child == tw->tabs.topWidget )
+	  return ;
+
+	if( undraw )
+	  gc = tw->tabs.backgroundGC ;
+
+	else if( XtIsSensitive(child) )
+	{
+	  gc = tw->tabs.foregroundGC ;
+	  XSetForeground(dpy, gc, tab->tabs.foreground) ;
+	}
+	else
+	{
+	  gc = tw->tabs.greyGC ;
+	  XSetForeground(dpy, gc, tab->tabs.grey) ;
+	}
+
+	points[0].x = x+1 ; points[0].y = y+hgt-1 ;
+	points[1].x = x+1 ; points[1].y = y+2 ;
+	points[2].x = x+2 ; points[2].y = y+1 ;
+	points[3].x = x+wid-4 ; points[3].y = y+1 ;
+	points[4].x = x+wid-3 ; points[4].y = y+2 ;
+	points[5].x = x+wid-3 ; points[5].y = y+hgt-1 ;
+
+	XDrawLines(dpy,win,gc, points,6, CoordModeOrigin) ;
+}
+
+
+/* Undraw one tab interior */
+
+static	void
+UndrawTab(TabsWidget tw, Widget child)
+{
+	TabsConstraints tab = (TabsConstraints)child->core.constraints;
+	Position	x = tab->tabs.x ;
+	Position	y = tab->tabs.y ;
+	Dimension	twid = tab->tabs.width ;
+	Dimension	thgt = tw->tabs.tab_height ;
+	Display		*dpy = XtDisplay((Widget)tw) ;
+	Window		win = XtWindow((Widget)tw) ;
+	GC		bggc = tw->tabs.backgroundGC ;
+
+	XFillRectangle(dpy,win,bggc, x,y, twid,thgt) ;
+}
+
+
+
+
+
+	/* GEOMETRY UTILITIES */
+
+	/* Overview:
+	 *
+	 *  MaxChild(): ask all children (except possibly one) their
+	 *  preferred sizes, set max_cw, max_ch accordingly.
+	 *
+	 *  GetPreferredSizes(): ask all children their preferred sizes,
+	 *  set max_cw, max_ch accordingly.
+	 *
+	 *  PreferredSize(): given max_cw, max_ch, return tabs widget
+	 *  preferred size.  Iterate with other widths in order to get
+	 *  a reasonable aspect ratio.
+	 *
+	 *  PreferredSize2(): Given child dimensions, return Tabs
+	 *  widget dimensions.
+	 *
+	 *  PreferredSize3(): Same, except given child dimensions plus
+	 *  shadow.
+	 */
+
+
+	/* Compute the width of one child's tab.  Positions will be computed
+	 * elsewhere.
+	 *
+	 *	height: font height + vertical_space*2 + shadowWid*2
+	 *	width:	string width + horizontal_space*2 + shadowWid*2
+	 *
+	 * All tabs are the same height, so that is computed elsewhere.
+	 */
+
+static	void
+TabWidth(Widget w)
+{
+	TabsConstraints tab = (TabsConstraints) w->core.constraints ;
+	TabsWidget	tw = (TabsWidget)XtParent(w) ;
+	String		lbl = tab->tabs.label != NULL ?
+				tab->tabs.label : XtName(w) ;
+	XFontStruct	*font = tw->tabs.font ;
+	int		iw = tw->tabs.internalWidth ;
+
+	tab->tabs.width = iw + SHADWID*2 ;
+	tab->tabs.l_x = tab->tabs.lbm_x = SHADWID + iw ;
+
+	if( tab->tabs.left_bitmap != None )
+	{
+	  tab->tabs.width += tab->tabs.lbm_width + iw ;
+	  tab->tabs.l_x += tab->tabs.lbm_width + iw ;
+	  tab->tabs.lbm_y = (tw->tabs.tab_height - tab->tabs.lbm_height)/2 ;
+	}
+
+	if( lbl != NULL && font != NULL )
+	{
+	  tab->tabs.width += XTextWidth( font, lbl, (int)strlen(lbl) ) + iw ;
+	  tab->tabs.l_y = (tw->tabs.tab_height +
+		 tw->tabs.font->max_bounds.ascent -
+		 tw->tabs.font->max_bounds.descent)/2 ;
+	}
+}
+
+
+
+	/* Lay out tabs to fit in given width.  Compute x,y position and
+	 * row number for each tab.  Return number of rows and total height
+	 * required by all tabs.  If there is only one row, add TABDELTA
+	 * height to the total.  Rows are assigned bottom to top.
+	 *
+	 * Tabs are indented from the edges by INDENT.
+	 *
+	 * TODO: if they require more than two rows and the total height:width
+	 * ratio is more than 2:1, then try something else.
+	 */
+
+static	int
+TabLayout(TabsWidget tw, int wid, int hgt, Dimension *reply_height, Bool query_only)
+{
+	int		i, row ;
+	int		num_children = tw->composite.num_children ;
+	Widget		*childP ;
+	Dimension	w ;
+	Position	x,y ;
+	TabsConstraints	tab ;
+
+	if (!query_only)
+	  tw->tabs.displayChildren = 0;
+
+	/* Algorithm: loop through children, assign X positions.  If a tab
+	 * would extend beyond the right edge, start a new row.  After all
+	 * rows are assigned, make a second pass and assign Y positions.
+	 */
+
+	if( num_children > 0 )
+	{
+	  /* Loop through the tabs and see how much space they need. */
+
+	  row = 0 ;
+	  x = INDENT ;
+	  y = 0 ;
+	  wid -= INDENT ;
+	  for(i=num_children, childP=tw->composite.children; --i >= 0; ++childP)
+	    if( XtIsManaged(*childP) )
+	    {
+	      tab = (TabsConstraints) (*childP)->core.constraints ;
+	      w = tab->tabs.width ;
+	      if( x + w > wid ) {			/* new row */
+		if (y + tw->tabs.tab_height > hgt)
+		  break;
+		++row ;
+		x = INDENT ;
+		y += tw->tabs.tab_height ;
+	      }
+	      if( !query_only ) {
+		tab->tabs.x = x ;
+		tab->tabs.y = y ;
+		tab->tabs.row = row ;
+	      }
+	      x += w + SPACING ;
+	      if (!query_only)
+		tw->tabs.displayChildren++;
+	    }
+	  /* If there was only one row, increse the height by TABDELTA */
+	  if( ++row == 1 )
+	  {
+	    y = TABDELTA ;
+	    if( !query_only )
+	      for(i=num_children, childP=tw->composite.children;
+		    --i >= 0 ; ++childP)
+		if( XtIsManaged(*childP) )
+		{
+		  tab = (TabsConstraints) (*childP)->core.constraints ;
+		  tab->tabs.y = y ;
+		}
+	  }
+	  y += tw->tabs.tab_height ;
+	}
+	else
+	  row = y = 0 ;
+
+	if( !query_only ) {
+	  tw->tabs.tab_total = y ;
+	  tw->tabs.numRows = row ;
+	}
+
+	if( reply_height != NULL )
+	  *reply_height = y ;
+
+	return row ;
+}
+
+
+
+	/* Find max preferred child size.  Returned sizes include child
+	 * border widths.
+	 */
+
+static	void
+GetPreferredSizes(TabsWidget tw)
+{
+	MaxChild(tw, NULL, 0,0) ;
+}
+
+
+
+	/* Find max preferred child size.  Returned sizes include child
+	 * border widths.  If except is non-null, don't ask that one.
+	 */
+
+static	void
+MaxChild(TabsWidget tw, Widget except, Dimension cw, Dimension ch)
+{
+	int			i ;
+	Widget			*childP = tw->composite.children ;
+	XtWidgetGeometry	preferred ;
+
+	for(i=tw->composite.num_children; --i >=0; ++childP)
+	  if( XtIsManaged(*childP)  &&  *childP != except )
+	  {
+	    (void) XtQueryGeometry(*childP, NULL, &preferred) ;
+	    cw = Max(cw, preferred.width + preferred.border_width * 2 ) ;
+	    ch = Max(ch, preferred.height + preferred.border_width * 2 ) ;
+	  }
+
+	tw->tabs.max_cw = cw ;
+	tw->tabs.max_ch = ch ;
+}
+
+
+
+	/* rotate row numbers to bring current widget to bottom row,
+	 * compute y positions for all tabs
+	 */
+
+static	void
+TabsShuffleRows(TabsWidget tw)
+{
+	TabsConstraints	tab ;
+	int		move ;
+	int		nrows ;
+	Widget		*childP ;
+	Dimension	th = tw->tabs.tab_height ;
+	Position	bottom ;
+	int		i ;
+
+	/* There must be a top widget.  If not, assign one. */
+	if( tw->tabs.topWidget == NULL && tw->composite.children != NULL )
+	  for(i=tw->composite.num_children, childP=tw->composite.children;
+	      --i >= 0;
+	      ++childP)
+	    if( XtIsManaged(*childP) ) {
+	      tw->tabs.topWidget = *childP ;
+	      break ;
+	    }
+
+	if( tw->tabs.topWidget != NULL )
+	{
+	  nrows = tw->tabs.numRows ;
+	  assert( nrows > 0 ) ;
+
+	  if( nrows > 1 )
+	  {
+	    tab = (TabsConstraints) tw->tabs.topWidget->core.constraints ;
+	    assert( tab != NULL ) ;
+
+	    /* how far to move top row */
+	    move = nrows - tab->tabs.row ;
+	    bottom = tw->tabs.tab_total - th ;
+
+	    for(i=tw->tabs.displayChildren, childP=tw->composite.children;
+		  --i >= 0;
+		  ++childP)
+	      if( XtIsManaged(*childP) )
+	      {
+		tab = (TabsConstraints) (*childP)->core.constraints ;
+		tab->tabs.row = (tab->tabs.row + move) % nrows ;
+		tab->tabs.y = bottom - tab->tabs.row * th ;
+	      }
+	  }
+	}
+}
+
+
+	/* Find preferred size.  Ask children, find size of largest,
+	 * add room for tabs & return.  This can get a little involved,
+	 * as we don't want to have too many rows of tabs; we may widen
+	 * the widget to reduce # of rows.
+	 *
+	 * This function requires that max_cw, max_ch already be set.
+	 */
+
+static	int
+PreferredSize(
+	TabsWidget	tw,
+	Dimension	*reply_width,		/* total widget size */
+	Dimension	*reply_height,
+	Dimension	*reply_cw,		/* child widget size */
+	Dimension	*reply_ch)
+{
+	Dimension	cw,ch ;		/* child width, height */
+	Dimension	wid,hgt ;
+	Dimension	rwid,rhgt ;
+	int		nrow ;
+
+	wid = cw = tw->tabs.max_cw ;
+	hgt = ch = tw->tabs.max_ch ;
+
+	nrow = PreferredSize2(tw, wid,hgt, &rwid, &rhgt) ;
+
+	/* Check for absurd results (more than 2 rows, high aspect
+	 * ratio).  Try wider size if needed.
+	 * TODO: make sure this terminates.
+	 */
+
+	if( nrow > 2 && rhgt > rwid )
+	{
+	  Dimension w0, w1 ;
+	  int maxloop = 20 ;
+
+	  /* step 1: start doubling size until it's too big */
+	  do {
+	    w0 = wid ;
+	    wid = Max(wid*2, wid+20) ;
+	    nrow = PreferredSize2(tw, wid,hgt, &rwid,&rhgt) ;
+	  } while( nrow > 2 && rhgt > rwid ) ;
+	  w1 = wid ;
+
+	  /* step 2: use Newton's method to find ideal size.  Stop within
+	   * 8 pixels.
+	   */
+	  while( --maxloop > 0 && w1 > w0 + 8 )
+	  {
+	    wid = (w0+w1)/2 ;
+	    nrow = PreferredSize2(tw, wid,hgt, &rwid,&rhgt) ;
+	    if( nrow > 2 && rhgt > rwid )
+	      w0 = wid ;
+	    else
+	      w1 = wid ;
+	  }
+	  wid = w1 ;
+	}
+
+	*reply_width = rwid ;
+	*reply_height = rhgt ;
+	if( reply_cw != NULL ) *reply_cw = cw ;
+	if( reply_ch != NULL ) *reply_ch = ch ;
+	return nrow ;
+}
+
+
+	/* Find preferred size, given size of children. */
+
+static	int
+PreferredSize2(
+	TabsWidget	tw,
+	int		cw,		/* child width, height */
+	int		ch,
+	Dimension	*reply_width,	/* total widget size */
+	Dimension	*reply_height)
+{
+	Dimension	s = SHADWID ;
+
+	/* make room for shadow frame */
+	cw += s*2 ;
+	ch += s*2 ;
+
+	return PreferredSize3(tw, cw, ch, reply_width, reply_height) ;
+}
+
+
+	/* Find preferred size, given size of children+shadow. */
+
+static	int
+PreferredSize3(
+	TabsWidget	tw,
+	int		wid,		/* child width, height */
+	int		hgt,
+	Dimension	*reply_width,	/* total widget size */
+	Dimension	*reply_height)
+{
+	Dimension	th ;		/* space used by tabs */
+	int		nrows ;
+
+	if( tw->composite.num_children > 0 )
+	  nrows = TabLayout(tw, wid, hgt, &th, True) ;
+	else {
+	  th = 0 ;
+	  nrows = 0 ;
+	}
+
+	*reply_width = Max(wid, MIN_WID) ;
+	*reply_height = Max(th+hgt, MIN_HGT) ;
+
+	return nrows ;
+}
+
+
+static	void
+MakeSizeRequest(TabsWidget tw)
+{
+	Widget			w = (Widget)tw ;
+	XtWidgetGeometry	request, reply ;
+	XtGeometryResult	result ;
+	Dimension		cw,ch ;
+
+	request.request_mode = CWWidth | CWHeight ;
+	PreferredSize(tw, &request.width, &request.height, &cw, &ch) ;
+
+	if( request.width == tw->core.width &&
+	    request.height == tw->core.height )
+	  return ;
+
+	result = XtMakeGeometryRequest(w, &request, &reply) ;
+
+	if( result == XtGeometryAlmost )
+	{
+	  /* Bugger.  Didn't get what we want, but were offered a
+	   * compromise.  If the width was too small, recompute
+	   * based on the too-small width and try again.
+	   * If the height was too small, make a wild-ass guess
+	   * at a wider width and try again.
+	   */
+
+	  if( reply.width < request.width && reply.height >= request.height )
+	  {
+	    Dimension s = SHADWID ;
+	    ch += s*2 ;
+	    PreferredSize3(tw, reply.width,ch, &request.width, &request.height);
+	    result = XtMakeGeometryRequest(w, &request, &reply) ;
+	    if( result == XtGeometryAlmost )
+	      (void) XtMakeGeometryRequest(w, &reply, NULL) ;
+	  }
+
+	  else
+	    (void) XtMakeGeometryRequest(w, &reply, NULL) ;
+	}
+}
+
+
+static	void
+getBitmapInfo(TabsWidget tw, TabsConstraints tab)
+{
+	Window root ;
+	int	x,y ;
+	unsigned int bw ;
+
+	if( tab->tabs.left_bitmap == None  ||
+	    !XGetGeometry(XtDisplay(tw), tab->tabs.left_bitmap, &root, &x, &y,
+		&tab->tabs.lbm_width, &tab->tabs.lbm_height,
+		&bw, &tab->tabs.lbm_depth) )
+	  tab->tabs.lbm_width = tab->tabs.lbm_height = 0 ;
+}
+
+
+
+
+	/* Code copied & modified from Gcs.c.  This version has dynamic
+	 * foreground.
+	 */
+
+static	void
+TabsAllocFgGC(TabsWidget tw)
+{
+	Widget		w = (Widget) tw;
+	XGCValues	values ;
+
+	values.background = tw->core.background_pixel ;
+	values.font = tw->tabs.font->fid ;
+	values.line_style = LineOnOffDash ;
+	values.line_style = LineSolid ;
+
+	tw->tabs.foregroundGC =
+	  XtAllocateGC(w, w->core.depth,
+	    GCBackground|GCFont|GCLineStyle, &values,
+	    GCForeground,
+	    GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
+		GCDashList|GCArcMode) ;
+}
+
+static	void
+TabsAllocGreyGC(TabsWidget tw)
+{
+	Widget		w = (Widget) tw;
+	XGCValues	values ;
+
+	values.background = tw->core.background_pixel ;
+	values.font = tw->tabs.font->fid ;
+#ifdef HAVE_XMU
+	if( tw->tabs.be_nice_to_cmap || w->core.depth == 1)
+	{
+	  values.fill_style = FillStippled ;
+	  tw->tabs.grey50 =
+	  values.stipple = XmuCreateStippledPixmap(XtScreen(w), 1L, 0L, 1) ;
+
+	  tw->tabs.greyGC =
+	    XtAllocateGC(w, w->core.depth,
+	      GCBackground|GCFont|GCStipple|GCFillStyle, &values,
+	      GCForeground,
+	      GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
+		  GCDashList|GCArcMode) ;
+	}
+	else
+#endif
+	{
+	  tw->tabs.greyGC =
+	    XtAllocateGC(w, w->core.depth,
+	      GCFont, &values,
+	      GCForeground,
+	      GCBackground|GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
+		  GCDashList|GCArcMode) ;
+	}
+}