/*
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */
/*
 * TabStack.c - Source code for the XmTabStack Widget Class.
 *
 * This file contains the source code that implements the XmTabStack
 * WidgetClass. The XmTabStack is a manager widget where each child of
 * the TabStack is associated with a label and/or pixmap that is
 * positioned on the side to simulate a folder tab.
 *
 */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "XmI.h"
#include <Xm/XmP.h>
#include <Xm/DialogS.h>
#include <Xm/BulletinBP.h>
#include <Xm/PushB.h>
#include <Xm/Separator.h>
#include <Xm/RowColumn.h>
#include <Xm/TabStackP.h>
#include <Xm/TabBoxP.h>
#include <Xm/VaSimpleP.h>

#undef TEAR_OFF_TABS

#ifdef TEAR_OFF_TABS
#include <Xm/DragCP.h>
#include <Xm/DragOverSP.h>
#include <Xm/DragDrop.h>
#include <Xm/DragIcon.h>
#endif

#ifdef _ARGS
#undef _ARGS
#endif
#ifndef _NO_PROTO
#define _ARGS(a) a
#else
#define _ARGS(a) ()
#endif

extern int _XiGetTabIndex _ARGS((Widget, int, int));
extern int _GetTabWidth _ARGS((Widget, int));
extern int _XmTabBoxGetTabHeight _ARGS((Widget, int));
extern Widget _XmTabBoxCanvas _ARGS((Widget));
extern void _XmTabBoxGetNumRowColumns _ARGS((Widget, int, int*, int*));
extern int _XmTabBoxGetMaxTabWidth _ARGS((Widget));
extern int _XmTabBoxGetMaxTabHeight _ARGS((Widget));
extern void _XmTabBoxSelectTab _ARGS((Widget, int));
extern void _XmTabBoxGetNumRowsColumns _ARGS((Widget, int, int *, int *));
extern void _XmTabBoxStackedGeometry _ARGS((Widget, int, XRectangle *));
extern int _XmTabbedStackListCount _ARGS((XmTabbedStackList));

#ifndef _NO_PROTO
static void ClassInitialize (void);
#else
static void ClassInitialize ();
#endif

#ifdef TEAR_OFF_TABS
static void ClassPartInitialize _ARGS((WidgetClass));
#endif
static void Initialize _ARGS((Widget, Widget, ArgList, Cardinal*));
static void Destroy _ARGS((Widget));
static void Resize _ARGS((Widget));
static void Realize _ARGS((Widget, XtValueMask *, XSetWindowAttributes *));
static void Redisplay _ARGS((Widget, XEvent*, Region));
static Boolean SetValues _ARGS((Widget, Widget, Widget, ArgList, Cardinal*));
static XtGeometryResult QueryGeometry _ARGS((Widget, XtWidgetGeometry*,
					    XtWidgetGeometry*));

static XtGeometryResult GeometryManager _ARGS((Widget, XtWidgetGeometry*,
					       XtWidgetGeometry*));
static void ChangeManaged _ARGS((Widget));

static void ConstraintInitialize _ARGS((Widget, Widget, ArgList, Cardinal*));
static void ConstraintDestroy _ARGS((Widget));
static Boolean ConstraintSetValues _ARGS((Widget, Widget, Widget, ArgList,
					  Cardinal*));

static Boolean CvtStringToXmTabSide _ARGS((Display*, XrmValue*, Cardinal*,
					   XrmValue*, XrmValue*, XtPointer*));
static Boolean CvtStringToXmPixmapPlacement _ARGS((Display*, XrmValue*,
						   Cardinal*, XrmValue*,
						   XrmValue*, XtPointer*));
static Boolean CvtStringToXiPixel _ARGS((Display*, XrmValue*, Cardinal*,
					 XrmValue*, XrmValue*, XtPointer*));
static Boolean CvtStringToXiPixmap _ARGS((Display*, XrmValue*, Cardinal*,
					  XrmValue*, XrmValue*, XtPointer*));

static void PickSizes _ARGS((XmTabStackWidget, int, int, XRectangle*, 
			     XRectangle*));
static void Layout _ARGS((XmTabStackWidget));
static void DrawShadows _ARGS((XmTabStackWidget, GC, GC, int, int, int, int));
static void DrawStackedShadows _ARGS((XmTabStackWidget, GC, GC, int, int,
				      int, int));

static void TabSelectedCallback _ARGS((Widget, XtPointer, XtPointer));
static void CheckSetRenderTable(Widget wid, int offs, XrmValue *value); 
#ifdef TEAR_OFF_TABS
static void TearOffCallback _ARGS((Widget, XtPointer, XtPointer));
static void XmTabStackMenu _ARGS((Widget, XEvent*, String*, Cardinal*));
static void BuildMenu _ARGS((XmTabStackWidget));
static void XmTabBoxDragTab _ARGS((Widget, XEvent*, String*, Cardinal*));
static Atom TabAtom _ARGS((Widget));
static void HandleTabDrop _ARGS((Widget, XtPointer, XtPointer));
static void TabTransferProc _ARGS((Widget, XtPointer, Atom*, Atom*,
				   XtPointer, unsigned long*, int));
static Boolean TabConvertProc _ARGS((Widget, Atom*, Atom*, Atom*,
				     XtPointer*, unsigned long*, int*,
				     unsigned long*, XtPointer, XtRequestId));
static void XiMoveTabPanel _ARGS((Widget, Widget));
#define WidgetAtom(w) XmInternAtom(XtDisplay(w), "WIDGET", False)
#endif


#define ValidPixmap(p) ((p) != (Pixmap)NULL && \
			(p) != (Pixmap)XmUNSPECIFIED_PIXMAP && \
			(p) != (Pixmap)XmPIXMAP_DYNAMIC)

#define SetSolidGC(d,g,p) \
{ \
      XGCValues _macro_gc_values; \
      _macro_gc_values.foreground = (p); \
      _macro_gc_values.fill_style = FillSolid; \
      XChangeGC((d),(g), GCForeground | GCFillStyle, &_macro_gc_values); \
}

#define SetTiledGC(d,g,p) \
{ \
      XGCValues _macro_gc_values; \
      _macro_gc_values.tile = (p); \
      _macro_gc_values.fill_style = FillTiled; \
      XChangeGC((d),(g), GCTile | GCFillStyle, &_macro_gc_values); \
}

#define XiBackgroundSpecified(c) \
	(XmTabStackC_tab_background(c) != XmCOLOR_DYNAMIC || \
	 ValidPixmap(XmTabStackC_tab_background_pixmap(c)))

#define SetChildGC(c,g) \
{ \
      if( ValidPixmap(XmTabStackC_tab_background_pixmap(c)) ) \
      { \
	    SetTiledGC(XtDisplay(c), (g), XmTabStackC_tab_background_pixmap(c)); \
      } \
      else \
      { \
	    SetSolidGC(XtDisplay(c), (g), XmTabStackC_tab_background(c)); \
      }\
}

#define XiSelectSpecified(t) \
	(XmTabStack_select_color((t)) != XmCOLOR_DYNAMIC || \
	 ValidPixmap(XmTabStack_select_pixmap((t))))

#define SetSelectGC(t,g) \
{ \
      if( ValidPixmap(XmTabStack_select_pixmap((t))) ) \
      { \
	    SetTiledGC(XtDisplay(t), (g), XmTabStack_select_pixmap((t))); \
      } \
      else \
      { \
	    SetSolidGC(XtDisplay(t), (g), XmTabStack_select_color((t))); \
      }\
}

#ifndef AssignMax
#define AssignMax(x,y) if((y)>(x)) (x)=(y); else
#endif
#ifndef AssignMin
#define AssignMin(x,y) if((y)<(x)) (x)=(y); else
#endif
#ifndef Max
#define Max(x,y) (((x)>(y))?(x):(y))
#endif

#define LocalTabBox(t) (XtParent(XmTabStack_tab_box((t))) == (Widget)(t))
#define IsTabBox(t,k) (XmTabStack_tab_box((t)) == (k))
#define IsValidChild(t, k) (XtIsManaged(k) && !(k)->core.being_destroyed && \
			    !IsTabBox(t,k))

//#define BBPart(w) (*(XmBulletinBoardPart*)((char*)(w) + XmTabStack_offsets[XmBulletinBoardIndex]))
#define BBPart(w) (((XmBulletinBoardWidget)(w))->bulletin_board)



#ifdef TEAR_OFF_TABS
static XtActionsRec actions[] = {
    { "XmTabStackMenu",		  (XtActionProc) XmTabStackMenu           },
    { "XmTabBoxDragTab",	  (XtActionProc) XmTabBoxDragTab          },
};
static char drag_translations[] = 
    "<Btn2Down>:   XmTabBoxArmTab() XmTabBoxDragTab()\n\
     <Btn3Down>:   XmTabStackMenu()\n\
     <Key>osfMenu: XmTabStackMenu()";
#endif

static XtConvertArgRec XmColorConvertArgs[] = {
    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.self),
	 sizeof(Widget)},
};

static String tab_stack_filter[] =  
    { XmNx, XmNy, XmNwidth, XmNheight, XmNdestroyCallback, XmNsensitive,
      XmNuserData, XmNnavigationType, XmNmarginWidth, XmNmarginHeight,
      XmNtabList, XmNtabOrientation, XmNorientation, XmNtabEdge,
      XmNselectCallback, XmNunselectCallback, NULL };

#ifdef TEAR_OFF_TABS
/*
 * Bitmap used for Drag Icon
 */
#define tab_pix_width 32
#define tab_pix_height 32
#define tab_pix_x_hot 0
#define tab_pix_y_hot 0
static unsigned char tab_pix_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0xa9, 0xea, 0x03, 0x00, 0x54, 0xd5,
   0xfb, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xe0, 0xaa, 0xaa, 0xaa, 0xba,
   0x54, 0x55, 0x55, 0xf5, 0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5,
   0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5, 0xaa, 0xaa, 0xaa, 0xba,
   0x54, 0x55, 0x55, 0xf5, 0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5,
   0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5, 0xaa, 0xaa, 0xaa, 0xba,
   0x54, 0x55, 0x55, 0xf5, 0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5,
   0xaa, 0xaa, 0xaa, 0xba, 0x54, 0x55, 0x55, 0xf5, 0xfe, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

static unsigned char tab_mask_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0xec, 0xff, 0xff, 0x3f,
  0xfc, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };

/*
static unsigned char invalid_bits[] = {
  0x00, 0xf0, 0x0f, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x00, 0x01, 0x80, 0x00,
  0xff, 0x00, 0x00, 0xe3, 0x3f, 0xe0, 0x07, 0xec, 0x13, 0x1c, 0x3c, 0xd8,
  0x0b, 0xfe, 0x7f, 0xb0, 0x08, 0x01, 0x20, 0xf0, 0x86, 0xaa, 0x1a, 0xa0,
  0x42, 0x55, 0x0d, 0xc2, 0xa2, 0xaa, 0x06, 0xc7, 0x62, 0x55, 0x83, 0xc5,
  0xa1, 0xaa, 0xc1, 0x86, 0x51, 0xd5, 0x60, 0x8d, 0xb1, 0x6a, 0xb0, 0x8a,
  0x51, 0x35, 0x58, 0x8d, 0xb1, 0x1a, 0xac, 0x8a, 0x51, 0x0d, 0x56, 0x8d,
  0xb1, 0x06, 0xab, 0x8a, 0x61, 0x83, 0x55, 0x85, 0xa2, 0xc1, 0xaa, 0xc6,
  0xe2, 0x60, 0x55, 0xc5, 0x42, 0xb0, 0xaa, 0xc2, 0x04, 0x58, 0x55, 0xe1,
  0x0a, 0xac, 0xaa, 0xb0, 0x0c, 0x56, 0x55, 0xf0, 0x1e, 0xfc, 0x3f, 0xf8,
  0x20, 0xe0, 0x07, 0x04, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00,
  0x00, 0x0e, 0x70, 0x00, 0x00, 0xf0, 0x0f, 0x00, };

static unsigned char invalid_mask_bits[] = {
  0x00, 0xf0, 0x0f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0xff, 0x00,
  0xc0, 0xff, 0xff, 0x1f, 0xe0, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x3f,
  0xfc, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
  0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x3f,
  0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f,
  0xe0, 0xff, 0xff, 0x07, 0xc0, 0xff, 0xff, 0x03, 0x00, 0xff, 0xff, 0x00,
  0x00, 0xfe, 0x7f, 0x00, 0x00, 0xf0, 0x0f, 0x00, };
*/
static unsigned char invalid_bits[] = {
  0x00, 0xf0, 0x0f, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x00, 0x01, 0x80, 0x00,
  0xc0, 0x00, 0x00, 0x03, 0x20, 0xe0, 0x07, 0x04, 0x10, 0x1c, 0x38, 0x08,
  0x08, 0x02, 0x40, 0x10, 0x08, 0x01, 0x20, 0x10, 0x86, 0x00, 0x10, 0x20,
  0x42, 0x00, 0x08, 0x42, 0x22, 0x00, 0x04, 0x45, 0x22, 0x00, 0x82, 0x44,
  0x21, 0x00, 0x41, 0x84, 0x11, 0x80, 0x20, 0x88, 0x11, 0x40, 0x10, 0x88,
  0x11, 0x20, 0x08, 0x88, 0x11, 0x10, 0x04, 0x88, 0x11, 0x08, 0x02, 0x88,
  0x11, 0x04, 0x01, 0x88, 0x21, 0x82, 0x00, 0x84, 0x22, 0x41, 0x00, 0x44,
  0xa2, 0x20, 0x00, 0x44, 0x42, 0x10, 0x00, 0x42, 0x04, 0x08, 0x00, 0x21,
  0x08, 0x04, 0x80, 0x10, 0x08, 0x02, 0x40, 0x10, 0x10, 0x1c, 0x38, 0x08,
  0x20, 0xe0, 0x07, 0x04, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00,
  0x00, 0x0e, 0x70, 0x00, 0x00, 0xf0, 0x0f, 0x00, };

static unsigned char invalid_mask_bits[] = {
  0x00, 0xf0, 0x0f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0xff, 0x00,
  0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, 0xf0, 0x1f, 0xf8, 0x0f,
  0xf8, 0x03, 0xc0, 0x1f, 0xf8, 0x01, 0xe0, 0x1f, 0xfe, 0x00, 0xf0, 0x3f,
  0x7e, 0x00, 0xf8, 0x7f, 0x3e, 0x00, 0xfc, 0x7d, 0x3e, 0x00, 0xfe, 0x7c,
  0x3f, 0x00, 0x7f, 0xfc, 0x1f, 0x80, 0x3f, 0xf8, 0x1f, 0xc0, 0x1f, 0xf8,
  0x1f, 0xe0, 0x0f, 0xf8, 0x1f, 0xf0, 0x07, 0xf8, 0x1f, 0xf8, 0x03, 0xf8,
  0x1f, 0xfc, 0x01, 0xf8, 0x3f, 0xfe, 0x00, 0xfc, 0x3e, 0x7f, 0x00, 0x7c,
  0xbe, 0x3f, 0x00, 0x7c, 0xfe, 0x1f, 0x00, 0x7e, 0xfc, 0x0f, 0x00, 0x3f,
  0xf8, 0x07, 0x80, 0x1f, 0xf8, 0x03, 0xc0, 0x1f, 0xf0, 0x1f, 0xf8, 0x0f,
  0xe0, 0xff, 0xff, 0x07, 0xc0, 0xff, 0xff, 0x03, 0x00, 0xff, 0xff, 0x00,
  0x00, 0xfe, 0x7f, 0x00, 0x00, 0xf0, 0x0f, 0x00, };
#endif

static XtResource resources[] =
{
  /* Inherit (but changed default) resources */
  {
    XmNshadowThickness, XmCShadowThickness, XmRDimension,
    sizeof(Dimension), XtOffsetOf(XmManagerRec, manager.shadow_thickness),
    XmRImmediate, (XtPointer) 2
  },

  {
    XmNautoUnmanage, XmCAutoUnmanage, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmBulletinBoardRec, bulletin_board.auto_unmanage),
    XmRImmediate, (XtPointer) False
  },

  /* TabBox Resources */
#ifdef TEAR_OFF_TABS
  {
    XmNallowTearOffTabs, XmCAllowTearOffTabs, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.allow_tear_offs),
    XmRImmediate, (XtPointer) True
  },

  {
    XmNtearOffLabelString, XmCTearOffLabelString, XmRXmString,
    sizeof(XmString), XtOffsetOf(XmTabStackRec, tab_stack.tear_off_label),
    XmRString, (XtPointer) "Tear Off Tab"
  },
#endif
  {
    XmNtabAutoSelect, XmCTabAutoSelect, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.tab_auto_select),
    XmRImmediate, (XtPointer) True
  },

  {
    XmNtabStyle, XmCTabStyle, XmRTabStyle,
    sizeof(XmTabStyle), XtOffsetOf(XmTabStackRec, tab_stack.tab_style),
    XmRImmediate, (XtPointer) XmTABS_BEVELED
  },

  {
    XmNtabMode, XmCTabMode, XmRTabMode,
    sizeof(XmTabMode), XtOffsetOf(XmTabStackRec, tab_stack.tab_mode),
    XmRImmediate, (XtPointer) XmTABS_BASIC
  },

  {
    XmNtabMarginWidth, XmCMarginWidth, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmTabStackRec, tab_stack.tab_margin_width),
    XmRImmediate, (XtPointer) 3
  },

  {
    XmNtabMarginHeight, XmCMarginHeight, XmRVerticalDimension,
    sizeof(Dimension), XtOffsetOf(XmTabStackRec, tab_stack.tab_margin_height),
    XmRImmediate, (XtPointer) 3
  },

  {
    "pri.vate", "Pri.vate", XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.check_set_render_table),
    XmRImmediate, (XtPointer) False
  },
  
  {
    XmNfontList, XmCFontList, XmRFontList,
    sizeof (XmFontList), XtOffsetOf(XmTabStackRec, tab_stack.font_list),
    XmRCallProc, (XtPointer) CheckSetRenderTable
  },
  
  {
    XmNrenderTable, XmCRenderTable, XmRRenderTable,
    sizeof(XmRenderTable), XtOffsetOf(XmTabStackRec, tab_stack.font_list),
    XmRCallProc, (XtPointer) CheckSetRenderTable
  },

  {
    XmNhighlightThickness, XmCHighlightThickness, XmRDimension,
    sizeof(Dimension), XtOffsetOf(XmTabStackRec, tab_stack.highlight_thickness),
    XmRImmediate, (XtPointer) 2
  },

  {
    XmNtabSide, XmCTabSide, XmRXmTabSide,
    sizeof(XmTabSide), XtOffsetOf(XmTabStackRec, tab_stack.tab_side),
    XmRImmediate, (XtPointer) XmTABS_ON_TOP
  },

  {
    XmNtabOrientation, XmCTabOrientation, XmRTabOrientation,
    sizeof(XmTabOrientation), XtOffsetOf(XmTabStackRec, tab_stack.tab_orientation),
    XmRImmediate, (XtPointer) XmTAB_ORIENTATION_DYNAMIC
  },

  {
    XmNuniformTabSize, XmCUniformTabSize, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.uniform_tab_size),
    XmRImmediate, (XtPointer) True
  },

  {
    XmNtabSelectColor, XmCTabSelectColor, XmRXmPixel,
    sizeof(Pixel), XtOffsetOf(XmTabStackRec, tab_stack.select_color),
    XmRImmediate, (XtPointer) XmCOLOR_DYNAMIC
  },

  {
    XmNtabSelectPixmap, XmCTabSelectPixmap, XmRXmPixmap,
    sizeof(Pixmap), XtOffsetOf(XmTabStackRec, tab_stack.select_pixmap),
    XmRImmediate, (XtPointer) XmUNSPECIFIED_PIXMAP
  },

  {
    XmNstackedEffect, XmCStackedEffect, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.stacked_effect),
    XmRImmediate, (XtPointer) True
  },

  {
    XmNtabCornerPercent, XmCTabCornerPercent, XmRInt,
    sizeof(int), XtOffsetOf(XmTabStackRec, tab_stack.tab_corner_percent),
    XmRImmediate, (XtPointer) 40
  },

  {
    XmNtabLabelSpacing, XmCTabLabelSpacing, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmTabStackRec, tab_stack.tab_label_spacing),
    XmRImmediate, (XtPointer) 2
  },

  {
    XmNtabOffset, XmCTabOffset, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmTabStackRec, tab_stack.tab_offset),
    XmRImmediate, (XtPointer) 10
  },

  {
    XmNuseImageCache, XmCUseImageCache, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackRec, tab_stack.use_image_cache),
    XmRImmediate, (XtPointer) True
  },

  {
    XmNtabSelectedCallback, XmCCallback, XmRCallback,
    sizeof(XtCallbackList), XtOffsetOf(XmTabStackRec, tab_stack.tab_select_callback),
    XmRImmediate, (XtPointer) NULL
  },

  /* appears to be intentionally undocumented; interface not yet public */
  {
    XmNtabBoxWidget, XmCWidget, XmRWidget,
    sizeof(Widget), XtOffsetOf(XmTabStackRec, tab_stack.tab_box),
    XmRImmediate, (XtPointer) NULL
  }
};

static XmSyntheticResource get_resources[] =
{
  {
    XmNtabMarginWidth, sizeof(Dimension),
    XtOffsetOf(XmTabStackRec, tab_stack.tab_margin_width),
    XmeFromHorizontalPixels, (XmImportProc) XmeToHorizontalPixels
  },

  {
    XmNtabMarginHeight, sizeof(Dimension),
    XtOffsetOf(XmTabStackRec, tab_stack.tab_margin_height),
    XmeFromVerticalPixels, (XmImportProc) XmeToVerticalPixels
  },

  {
    XmNtabLabelSpacing, sizeof(Dimension),
    XtOffsetOf(XmTabStackRec, tab_stack.tab_label_spacing),
    XmeFromHorizontalPixels, (XmImportProc) XmeToHorizontalPixels
  },

  {
    XmNtabOffset, sizeof(Dimension),
    XtOffsetOf(XmTabStackRec, tab_stack.tab_offset),
    XmeFromHorizontalPixels, (XmImportProc) XmeToHorizontalPixels
  }
};

static XtResource constraint_resources[] =
{
  {
    XmNtabLabelString, XmCTabLabelString, XmRXmString,
    sizeof(XmString), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_label_string),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNtabAlignment, XmCAlignment, XmRAlignment,
    sizeof(unsigned char), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_alignment),
    XmRImmediate, (XtPointer) XmALIGNMENT_CENTER
  },

  {
    XmNtabStringDirection, XmCStringDirection, XmRStringDirection,
    sizeof(unsigned char), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_string_direction),
    XmRImmediate, (XtPointer) XmSTRING_DIRECTION_DEFAULT
  },

  {
    XmNtabLabelPixmap, XmCTabLabelPixmap, XmRManForegroundPixmap,
    sizeof(Pixmap), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_label_pixmap),
    XmRImmediate, (XtPointer) XmUNSPECIFIED_PIXMAP
  },

  {
    XmNtabPixmapPlacement, XmCTabPixmapPlacement, XmRXmPixmapPlacement,
    sizeof(XmPixmapPlacement), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_pixmap_placement),
    XmRImmediate, (XtPointer) XmPIXMAP_RIGHT
  },

  {
    XmNfreeTabPixmap, XmCFreeTabPixmap, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackConstraintRec, tab_stack.free_tab_pixmap),
    XmRImmediate, (XtPointer) False
  },
  
  {
    XmNtabBackground, XmCBackground, XmRXmPixel,
    sizeof(Pixel), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_background),
    XmRImmediate, (XtPointer) XmCOLOR_DYNAMIC
  },

  {
    XmNtabBackgroundPixmap, XmCBackgroundPixmap, XmRXmPixmap,
    sizeof(Pixmap), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_background_pixmap),
    XmRImmediate, (XtPointer) XmPIXMAP_DYNAMIC
  },

  {
    XmNtabForeground, XmCForeground, XmRXmPixel,
    sizeof(Pixel), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_foreground),
    XmRImmediate, (XtPointer) XmCOLOR_DYNAMIC
  },
#ifdef TEAR_OFF_TABS
  {
    XmNtabTearOffEnabled, XmCTabTearOffEnabled, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmTabStackConstraintRec, tab_stack.tear_off_enabled),
    XmRImmediate, (XtPointer) True
  }
#endif
};

static void Get_tabLabelString(Widget, int, XtArgVal *);
static XmSyntheticResource cont_get_resources[] =
{
  {
    XmNtabLabelString, sizeof(XmString),
    XtOffsetOf(XmTabStackConstraintRec, tab_stack.tab_label_string),
    Get_tabLabelString, (XmImportProc) NULL
  }
};

/* ARGSUSED */
static void Get_tabLabelString (Widget widget, int offset, XtArgVal *value)
{
    (*value) = (XtArgVal) XmStringCopy(XmTabStackC_tab_label_string(widget));
}

XmTabStackClassRec xmTabStackClassRec = {
  { /* Core Fields */
    /* superclass	  */	(WidgetClass) &xmBulletinBoardClassRec,
    /* class_name	  */	"XmTabStack",
    /* widget_size	  */	sizeof(XmTabStackRec),
    /* class_initialize   */    ClassInitialize,
#ifdef TEAR_OFF_TABS
    /* class_part_initial */	ClassPartInitialize,
#else
    /* class_part_initial */	NULL,
#endif
    /* class_inited       */	False,
    /* initialize	  */	Initialize,
    /* initialize hook    */    NULL,
    /* realize		  */	Realize,
#ifdef TEAR_OFF_TABS
    /* actions		  */	actions,
    /* num_actions	  */	XtNumber(actions),
#else
    /* actions		  */	NULL,
    /* num_actions	  */	0,
#endif
    /* resources	  */	(XtResource*)resources,
    /* num_resources	  */	XtNumber(resources),
    /* xrm_class	  */	NULLQUARK,
    /* compress_motion	  */	True,
    /* compress_exposure  */	XtExposeCompressMultiple,
    /* compress enter/exit*/    True,
    /* visible_interest	  */	False,
    /* destroy		  */	Destroy,
    /* resize		  */	Resize,
    /* expose		  */	Redisplay,
    /* set_values	  */	SetValues,
    /* set values hook    */    NULL,
    /* set values almost  */    XtInheritSetValuesAlmost,
    /* get values hook    */    NULL,
    /* accept_focus	  */	NULL,
    /* version            */    XtVersion,
    /* callback offsetlst */    NULL,
    /* default trans      */    XtInheritTranslations,
    /* query geo proc	  */	QueryGeometry,
    /* display accelerator*/	NULL,
    /* extension record   */    (XtPointer) NULL },
  { /* Composite Fields */
    /* geometry_manager   */    GeometryManager,
    /* change_managed     */    ChangeManaged,
    /* insert_child       */    XtInheritInsertChild,
    /* delete_child       */    XtInheritDeleteChild,
    /* extension          */    (XtPointer) NULL },
  { /* Constraint Fields */
    /* resources          */    constraint_resources,
    /* num_resources      */    XtNumber(constraint_resources),
    /* constraint_size    */    sizeof (XmTabStackConstraintRec),
    /* initialize         */    ConstraintInitialize,
    /* destroy            */    ConstraintDestroy,
    /* set_values         */    ConstraintSetValues,
    /* extension          */    (XtPointer) NULL },
  { /* OSF/Motif Manager Fields */
    /* translations       */    XtInheritTranslations,
    /* syn_resources      */    get_resources,
    /* num_syn_resources  */    XtNumber(get_resources),
    /* syn_resources      */    cont_get_resources,
    /* num_syn_resources  */    XtNumber(cont_get_resources),
    /* parent_process     */    XmInheritParentProcess,
    /* extension          */    (XtPointer) NULL },
  { /* OSF/Motif BulletinBoard */
    /* always_install_acc */    False,
    /* geo_matrix_create  */    XmInheritGeoMatrixCreate,
    /* focus_moved_proc   */    XmInheritFocusMovedProc,
    /* extension          */    (XtPointer) NULL },
  { /* EPak Stack Box Fields */
#ifdef TEAR_OFF_TABS
    /* drag_translations  */    drag_translations,
#else
    /* drag_translations  */    "",
#endif
    /* extension          */    (XtPointer) NULL }
};

WidgetClass xmTabStackWidgetClass = (WidgetClass) &xmTabStackClassRec;

/*
 * Note these aren't static, even though they should be.  TabBox.c
 * uses the instance data too.
 */

/*
 * Function:
 *	ClassInitialize(void)
 * Description:
 *	This function sets all the type converters needed for the
 *	XmTabStack widget class.
 * Input:
 *	None.
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
ClassInitialize(void)
#else
ClassInitialize()
#endif
{
    XmTabStackClassRec* wc = &xmTabStackClassRec;

    /*
     * Initialize the XmTabBox class to add its type converters which we
     * also use.
     */
    XtInitializeWidgetClass(xmTabBoxWidgetClass);
    XtSetTypeConverter(XmRString, XmRXmTabSide,
		       CvtStringToXmTabSide, NULL, 0, XtCacheNone, NULL);
    XtSetTypeConverter(XmRString, XmRXmPixmapPlacement,
		       CvtStringToXmPixmapPlacement, NULL, 0, XtCacheNone,
		       NULL);
    XtSetTypeConverter(XmRString, XmRXmPixel,
		       CvtStringToXiPixel,
		       XmColorConvertArgs, XtNumber(XmColorConvertArgs),
		       XtCacheNone, NULL);
    XtSetTypeConverter(XmRString, XmRXmPixmap,
		       CvtStringToXiPixmap,
		       XmColorConvertArgs, XtNumber(XmColorConvertArgs),
		       XtCacheNone, NULL);
}

#ifdef TEAR_OFF_TABS
static void
#ifndef _NO_PROTO
ClassPartInitialize(WidgetClass widget_class)
#else
ClassPartInitialize(widget_class)
    WidgetClass widget_class;
#endif
{
    XmTabStackWidgetClass tab = (XmTabStackWidgetClass) widget_class;

    _XmFastSubclassInit (w_class, XmTABSTACK_BIT);

    if( XmTabStack_class(tab).drag_translations != NULL )
    {
	if( (String)XmTabStack_class(tab).drag_translations ==
	    XtInheritTranslations )
	{
	    XmTabStack_class(tab).drag_translations = (String)
		((XmTabStackWidgetClass) tab->core_class.superclass)->
		    tab_stack_class.drag_translations;
	}
	else
	{
	    XmTabStack_class(tab).drag_translations = (String)
		XtParseTranslationTable((String) XmTabStack_class(tab).
					drag_translations);
	}
    }
}
#endif

/*
 * Function:
 *	Initialize(request, set, arg_list, arg_cnt)
 * Description:
 *	This function initializes the widget data structure for an
 *	instance of the XmTabStack.
 * Input:
 *	request  : Widget    - the user's requested resources
 *	set      : Widget    - the initial resource values
 *	arg_list : ArgList   - the argument list passed in with creation
 *	arg_cnt  : Cardinal* - the number of arguments
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
Initialize(Widget request, Widget set, ArgList arg_list, Cardinal *arg_cnt)
#else
Initialize(request, set, arg_list, arg_cnt)
    Widget   request, set;
    ArgList  arg_list;
    Cardinal *arg_cnt;
#endif
{
    XmTabStackWidget  ts = (XmTabStackWidget) set;
    Arg               args[50];
    ArgList           filtered_args, merged_args;
    Cardinal          n = 0, num_filtered_args;
    XmTabOrientation  orientation = XmTabStack_tab_orientation(ts);

    XmTabStack__inited(ts) = False;

    /* By default call XmNtabSelectedCallback callbacks when tab is selected */
    XmTabStack_do_notify(ts) = True;

    /*
     * Lets make sure that our font list is not NULL.  If it is we will
     * set it to the appropriate default.
     */
    if( XmTabStack_font_list(ts) == NULL )
    {
	XmTabStack_font_list(ts) = XmeGetDefaultRenderTable(set, XmLABEL_FONTLIST);
    }
    XmTabStack_font_list(ts) = XmFontListCopy(XmTabStack_font_list(ts));
    XmTabStack__gc(ts) = NULL;

    /*
     * For our stack we need to create a TabBox, to do this we need to set
     * up some arguments depending on which side the caller wants the tabs
     * on.
     */
    switch( XmTabStack_tab_side(ts) )
    {
    case XmTABS_ON_TOP:
    default:
	XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
	if( orientation == XmTAB_ORIENTATION_DYNAMIC )
	{
	    XtSetArg(args[n], XmNtabOrientation, XmTABS_LEFT_TO_RIGHT); ++n;
	}
	else
	{
	    XtSetArg(args[n], XmNtabOrientation, orientation); ++n;
	}
	XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_BOTTOM_RIGHT); ++n;
	break;
    case XmTABS_ON_BOTTOM:
	XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
	if( orientation == XmTAB_ORIENTATION_DYNAMIC )
	{
	    XtSetArg(args[n], XmNtabOrientation, XmTABS_LEFT_TO_RIGHT); ++n;
	}
	else
	{
	    XtSetArg(args[n], XmNtabOrientation, orientation); ++n;
	}
	XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_TOP_LEFT); ++n;
	break;
    case XmTABS_ON_RIGHT:
	XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
	if( orientation == XmTAB_ORIENTATION_DYNAMIC )
	{
	    XtSetArg(args[n], XmNtabOrientation, XmTABS_TOP_TO_BOTTOM); ++n;
	}
	else
	{
	    XtSetArg(args[n], XmNtabOrientation, orientation); ++n;
	}
	XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_TOP_LEFT); ++n;
	break;
    case XmTABS_ON_LEFT:
	XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
	if( orientation == XmTAB_ORIENTATION_DYNAMIC )
	{
	    XtSetArg(args[n], XmNtabOrientation, XmTABS_BOTTOM_TO_TOP); ++n;
	}
	else
	{
	    XtSetArg(args[n], XmNtabOrientation, orientation); ++n;
	}
	XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_BOTTOM_RIGHT); ++n;
	break;
    }

    /*
     * Lets do a real quick check here.  If we are in a stacked mode
     * and XmNuniformTabSize is False, then lets set it to true.
     *
     * We have to add the XtSetArg for the default value of XmNuniformTabSize
     * here because we may override it later.
     */
    XtSetArg(args[n], XmNuniformTabSize, XmTabStack_uniform_tab_size(ts)); ++n;
    if( !XmTabStack_uniform_tab_size(ts) &&
        (XmTabStack_tab_mode(ts) == XmTABS_STACKED ||
	 XmTabStack_tab_mode(ts) == XmTABS_STACKED_STATIC) )
    {
	/*
	 * Well it appears that we need to issue a warning here, because
	 * the user tried to set XmNuniformTabSize to False, while in a
	 * stacked mode.
	 */
	XmTabStack_uniform_tab_size(ts) = True;
	XmeWarning(set, XmNillegalUniformTabSizeMsg);
	XtSetArg(args[n], XmNuniformTabSize, True); ++n;
    }
    
    /*
     * Lets make sure we pass certain resource down to our children, like
     * color and font specification. This way if they were set via app
     * defaults the correct thing will happen.
     */
    XtSetArg(args[n], XmNrenderTable, XmTabStack_font_list(ts)); ++n;
    XtSetArg(args[n], XmNbackground, ts->core.background_pixel); ++n;
    XtSetArg(args[n], XmNshadowThickness, ts->manager.shadow_thickness); ++n;
    XtSetArg(args[n], XmNtabMode, XmTabStack_tab_mode(ts)); ++n;
    XtSetArg(args[n], XmNtabStyle, XmTabStack_tab_style(ts)); ++n;
    XtSetArg(args[n], XmNtabAutoSelect, XmTabStack_tab_auto_select(ts)); ++n;
    XtSetArg(args[n], XmNtabSelectColor, XmTabStack_select_color(ts)); ++n;
    XtSetArg(args[n], XmNtabSelectPixmap, XmTabStack_select_pixmap(ts));++n;
    XtSetArg(args[n], XmNtabMarginWidth, XmTabStack_tab_margin_width(ts)); ++n;
    XtSetArg(args[n], XmNtabMarginHeight, XmTabStack_tab_margin_height(ts));++n;
    XtSetArg(args[n], XmNtabLabelSpacing, XmTabStack_tab_label_spacing(ts));++n;
    XtSetArg(args[n], XmNtabCornerPercent,
	     XmTabStack_tab_corner_percent(ts)); ++n;
    XtSetArg(args[n], XmNtabOffset, XmTabStack_tab_offset(ts)); ++n;
    XtSetArg(args[n], XmNhighlightThickness,
	     XmTabStack_highlight_thickness(ts)); ++n;
    XtSetArg(args[n], XmNuseImageCache, XmTabStack_use_image_cache(ts)); ++n;
    XtSetArg(args[n], XmNunitType, XmPIXELS); ++n;

    /*
     * We also want to filter the other arguments that the user has
     * assigned to us.  We will then merge these with the argument
     * list we created and pass this to the children we create.
     */
    _XmFilterArgs(arg_list, *arg_cnt, tab_stack_filter,
		  &filtered_args, &num_filtered_args);
    merged_args = XtMergeArgLists(filtered_args, num_filtered_args,
				  args, n);


    if( XmTabStack_tab_box(ts) == NULL )
    {
	XmTabStack_tab_box(ts) =
	    XtCreateManagedWidget("tabBox", xmTabBoxWidgetClass, set,
				  merged_args, n + num_filtered_args);
    }
    else
    {
	XtSetValues(XmTabStack_tab_box(ts),
		    merged_args, n + num_filtered_args);
    }

#ifdef TEAR_OFF_TABS
    if( XmTabStack_tear_off_label(ts) != NULL )
    {
	XmTabStack_tear_off_label(ts)
	  = XmStringCopy(XmTabStack_tear_off_label(ts));
    }

    canvas = XmTabBox__canvas(XmTabStack_tab_box((XmTabBoxWidget)ts));

    n = 0;
    XmTabStack__menu(ts) = XmCreatePopupMenu(canvas, "tabMenu", args, n);

    XtOverrideTranslations(canvas,
	 (XtTranslations)((XmTabStackWidgetClass)(ts->core.widget_class))
	 ->tab_stack_class.drag_translations);

    target = TabAtom(set);
    n = 0;
    XtSetArg(args[n], XmNdropSiteActivity, XmDROP_SITE_ACTIVE); ++n;
    XtSetArg(args[n], XmNdropSiteOperations, XmDROP_MOVE); ++n;
    XtSetArg(args[n], XmNdropSiteType, XmDROP_SITE_COMPOSITE); ++n;
    XtSetArg(args[n], XmNimportTargets, &target); ++n;
    XtSetArg(args[n], XmNnumImportTargets, 1); ++n;
    XtSetArg(args[n], XmNdropProc, HandleTabDrop); ++n;
    XmDropSiteRegister(set, args, n); n = 0;

    XtSetArg(args[n], XmNlabelString, XmTabStack_tear_off_label(ts)); ++n;
    XmTabStack__tear_off_button(ts) =
	XtCreateManagedWidget("tearOffTab", xmPushButtonWidgetClass,
			      XmTabStack__menu(ts), args, n);
    XtAddCallback(XmTabStack__tear_off_button(ts), XmNactivateCallback,
		  TearOffCallback, NULL);
    n = 0;
    (void) XtCreateManagedWidget("separator", xmSeparatorWidgetClass,
				 XmTabStack__menu(ts), NULL, 0);
#endif

    /*
     * Add a callback to the XmTabBox so that we know when to display
     * the correct child.
     */
    XtAddCallback(XmTabStack_tab_box(ts), XmNselectCallback,
		  TabSelectedCallback, NULL);

    /*
     * Now that we are done with these argument list lets deallocate
     * them.
     */
    XtFree((XtPointer)filtered_args);
    XtFree((XtPointer)merged_args);

    /*
     * Lets initialize some of the instances values.
     */
    XmTabStack__size(ts).x = -1;
    XmTabStack__active_child(ts) = (Widget) NULL;
    XmTabStack__tab_list(ts) = (XmTabbedStackList) NULL;
    XmTabStack__source_icon(ts) = NULL;
    XmTabStack__invalid_icon(ts) = NULL;
    XmTabStack__source_pixmap(ts) = XmUNSPECIFIED_PIXMAP;
    XmTabStack__source_mask(ts) = XmUNSPECIFIED_PIXMAP;
    XmTabStack__invalid_pixmap(ts) = XmUNSPECIFIED_PIXMAP;
    XmTabStack__invalid_mask(ts) = XmUNSPECIFIED_PIXMAP;
    XmTabStack__set_tab_list(ts) = False;

    /*
     * Finally if the caller did not assign us a geometry lets pick
     * an appropriate default.
     */
    if( request->core.width == 0 )
    {
	ts->core.width = 2 * (ts->manager.shadow_thickness +
			      BBPart(ts).margin_width) + 50;
    }
    if( request->core.height == 0 )
    {
	ts->core.height = 2 * (ts->manager.shadow_thickness +
			       BBPart(ts).margin_height) + 50;
    }

    /* Used to select tabs before they are realized */
    XmTabStack__selected_tab(ts) = NULL;
    XmTabStack__selected_notify(ts) = False;

    /*
     * Now that the widget is initialized lets flag that fact.
     */
    XmTabStack__inited(ts) = True;
}


/*
 * Function:
 *	Destroy(widget)
 * Description:
 *	Deallocates all the memory allocated during the lifetime of the
 * 	given widget instance.
 * Input:
 *	widget : Widget - the widget being destroyed.
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
Destroy(Widget widget)
#else
Destroy(widget)
    Widget widget;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) widget;

    /*
     * If we have a tab list lets deallocate it.
     */
    if( XmTabStack__tab_list(tab) != NULL )
    {
	XmTabbedStackListFree(XmTabStack__tab_list(tab));
    }

    XmFontListFree(XmTabStack_font_list(tab));

    if( XmTabStack__gc(tab) != NULL )
    {
	XFreeGC(XtDisplay(tab), XmTabStack__gc(tab));
    }

#ifdef TEAR_OFF_TABS
    if( XmTabStack_tear_off_label(tab) != NULL )
    {
	XmStringFree(XmTabStack_tear_off_label(tab));
    }

    if( ValidPixmap(XmTabStack__source_pixmap(tab)) )
    {
	XFreePixmap(XtDisplay(widget), XmTabStack__source_pixmap(tab));
    }

    if( ValidPixmap(XmTabStack__source_mask(tab)) )
    {
	XFreePixmap(XtDisplay(widget), XmTabStack__source_mask(tab));
    }

    if( ValidPixmap(XmTabStack__invalid_pixmap(tab)) )
    {
	XFreePixmap(XtDisplay(widget), XmTabStack__invalid_pixmap(tab));
    }

    if( ValidPixmap(XmTabStack__invalid_mask(tab)) )
    {
	XFreePixmap(XtDisplay(widget), XmTabStack__invalid_mask(tab));
    }
#endif
}

/*
 * Function:
 *	Realize(widget, XtValueMask *, XSetWindowAttributes *)
 * Description:
 *	A realize method is used to address a timing issue that blocks
 *	XmTabStackSelectTab() from working before a selected tab widget
 *	has been realized...
 * Input:
 *	widget : Widget - the widget being resized.
 *
 *
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
Realize(Widget w, XtValueMask *mask, XSetWindowAttributes *attr)
#else
Realize(w, mask, attr)
    Widget w;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) w;
    
    /* Call superclass realize method... */
    XtRealizeProc realize;

    _XmProcessLock();
    realize = xmTabStackWidgetClass->core_class.superclass->core_class.realize;
    _XmProcessUnlock();

    (*realize) (w, mask, attr);

    /* Now that we are realized, let's select that tab, finally... */
    if ((XmTabStack__selected_tab(tab) != NULL) &&
	!XmTabStack__selected_tab(tab)->core.being_destroyed)
    {
	XmTabStackSelectTab(XmTabStack__selected_tab(tab),
			    XmTabStack__selected_notify(tab));
    }

    /*
     * Lets create a GC that we will use for drawing.
     */
    XmTabStack__gc(tab) = XCreateGC(XtDisplay(tab), XtWindow(tab),
				   0, NULL);
}

/*
 * Function:
 *	Resize(widget)
 * Description:
 *	Handles everything when the XmTabStack is resized.  This means
 *	that is shifts the children around to their new locations and
 *	sizes.
 * Input:
 *	widget : Widget - the widget being resized.
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
Resize(Widget widget)
#else
Resize(widget)
    Widget widget;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) widget;
    Widget           active = XmTabStack__active_child(tab);
    XRectangle       *size = &(XmTabStack__size(tab));
    Boolean          stacked = False;
    int		     cnt = _XmTabbedStackListCount(XmTabStack__tab_list(tab));
    GC     	     gc = tab->manager.background_GC;

    if( XmTabStack_tab_mode(tab) == XmTABS_STACKED ||
	XmTabStack_tab_mode(tab) == XmTABS_STACKED_STATIC )
    {
	stacked = True;
    }

    /*
     * First lets relayout all our children.
     */
    Layout(tab);

    /*
     * If we are realized then we want to do something about our
     * shadows, like erase the old one and redraw them at their 
     * new location.
     */
    if( XtIsRealized(widget) )
    {
	if( stacked || size->x != -1 )
	{
	    if( active != NULL && XiBackgroundSpecified(active) )
	    {
		gc = XmTabStack__gc(tab);
		SetChildGC(active, gc);
	    }
	    if( (!stacked || cnt < 1) && size->x != -1 )
	    {
		DrawShadows(tab, gc, gc,
			    size->x, size->y, size->width, size->height);
	    }
	}
	
	if( stacked && active != NULL && cnt > 0 )
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   active->core.x - BBPart(tab).margin_width,
			   active->core.y - BBPart(tab).margin_height,
			   XtWidth(active) +
			   2*BBPart(tab).margin_width,
			   XtHeight(active)+
			   2*BBPart(tab).margin_height);
	}
	else
	{
	    DrawShadows(tab, tab->manager.top_shadow_GC,
			tab->manager.bottom_shadow_GC, 0, 0,
			XtWidth(widget), XtHeight(widget));
	}

	size->x = size->y = 0;
	size->width = XtWidth(widget);
	size->height = XtHeight(widget);
    }
}

/*
 * Function:
 *	Redisplay(widget, event, region)
 * Description:
 *	Draws all the visuals for the TabStack.
 * Input:
 *	widget : Widget  - the widget that needs redisplayed
 *	event  : XEvent* - the event causing the redisplay
 *	region : Region  - the region that is being redisplayed.
 * Output:
 *	None.
 */
static void
#ifndef _NO_PROTO
Redisplay(Widget widget, XEvent *event, Region region)
#else
Redisplay(widget, event, region)
    Widget widget;
    XEvent *event;
    Region region;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) widget;
    XRectangle             *size = &(XmTabStack__size(tab));
    Widget                 active = XmTabStack__active_child(tab);
    int                    cnt = _XmTabbedStackListCount(XmTabStack__tab_list(tab));
    Boolean		   stacked = False;

    if( XmTabStack_stacked_effect(tab) && active != NULL &&
        (XmTabStack_tab_mode(tab) == XmTABS_STACKED ||
	 XmTabStack_tab_mode(tab) == XmTABS_STACKED_STATIC) )
    {
	stacked = True;
    }

    /*
     * Now lets look at our active child and if they have a background
     * set lets color ourselves that background.
     */
	    
    if( (active != NULL) &&
        (XiBackgroundSpecified(active) || XiSelectSpecified(tab)) )
    {
	GC        gc = XmTabStack__gc(tab);

	if( XiSelectSpecified(tab) )
	{
	    SetSelectGC(tab, gc);
	}
	else
	{
	    SetChildGC(active, gc);
	}

	if( stacked )
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   active->core.x - BBPart(tab).margin_width,
			   active->core.y - BBPart(tab).margin_height,
			   XtWidth(active) +
			   2 * (BBPart(tab).margin_width),
			   XtHeight(active) +
			   2 * (BBPart(tab).margin_height));
	}
	else
	{
	    if( event == NULL || event->xany.type != Expose )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			       0, 0, XtWidth(tab), XtHeight(tab));
	    }
	    else
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			       event->xexpose.x, event->xexpose.y,
			       event->xexpose.width, event->xexpose.height);
	    }
	}
    }

    /*
     * next if we have any gadget children lets be sure to 
     * redisplay them.
     */
    XmeRedisplayGadgets(widget, event, region);

    /*
     * Now lets redray our shadows.
     */
    if( stacked && cnt > 0 )
    {
	DrawStackedShadows(tab, tab->manager.top_shadow_GC,
			   tab->manager.bottom_shadow_GC,
			   0, 0, XtWidth(active) +
			   (2 * BBPart(tab).margin_width),
			   XtHeight(active) +
			   (2 * BBPart(tab).margin_height));
    }
    else
    {
	DrawShadows(tab, tab->manager.top_shadow_GC,
		    tab->manager.bottom_shadow_GC, 0, 0, XtWidth(widget),
		    XtHeight(widget));
    }

    size->x = size->y = 0;
    size->width = XtWidth(widget);
    size->height = XtHeight(widget);
}

/*
 * Function:
 *	SetValues(current, request, set, arg_list, arg_cnt)
 * Description:
 *	Handles changes to the widget induced by resource settings.
 * Input:
 *	current  : Widget    - the current state of the widget
 *	request  : Widget    - the request made by the caller
 *	set      : Widget    - the widget as it will appear
 *	arg_list : ArgList   - the argument list to implement the changes
 *	arg_cnt  : Cardinal* - the number of arguments
 * Output:
 *	Boolean - True if a redisplay is needed, else False.
 */
#define rfield(f) (XmTabStack_##f(r_tab))
#define cfield(f) (XmTabStack_##f(c_tab))
#define sfield(f) (XmTabStack_##f(s_tab))
#define bcfield(f) \
    (((XmBulletinBoardWidget) c_tab)->bulletin_board.f)
#define bsfield(f) \
    (((XmBulletinBoardWidget) s_tab)->bulletin_board.f)
//#define bcfield(f) ((XmBulletinBoardPart*) \
//		    ((char*)c_tab + XmTabStack_offsets[XmBulletinBoardIndex]))->f
//#define bsfield(f) ((XmBulletinBoardPart*) \
//		    ((char*)s_tab + XmTabStack_offsets[XmBulletinBoardIndex]))->f
static Boolean
#ifndef _NO_PROTO
SetValues(Widget current, Widget request, Widget set, ArgList arg_list,
	  Cardinal *arg_cnt)
#else
SetValues(current, request, set, arg_list, arg_cnt)
    Widget   current, request, set;
    ArgList  arg_list;
    Cardinal *arg_cnt;
#endif
{
    XmTabStackWidget c_tab = (XmTabStackWidget) current,
                     s_tab = (XmTabStackWidget) set;
    Arg              args[25];
    ArgList          filtered_args, merged_args;
    int 		n = 0;
    Cardinal         num_filtered_args;
    Boolean          need_redraw = False, need_resize = False,
                     need_layout = False; 

    if( c_tab->manager.shadow_thickness != s_tab->manager.shadow_thickness )
    {
	need_resize = True;
	need_layout = True;
    }

    if( bcfield(margin_width) != bsfield(margin_width) ||
        bcfield(margin_height) != bsfield(margin_height) ||
        cfield(tab_side) != sfield(tab_side) ||
        cfield(tab_orientation) != sfield(tab_orientation) )
    {
	need_layout = True;
	need_resize = True;
    }

    if( cfield(select_color) != sfield(select_color) ||
        cfield(select_pixmap) != sfield(select_pixmap) )
    {
	need_redraw = True;
    }

    if( cfield(font_list) != sfield(font_list) )
    {
	XmFontListFree(cfield(font_list));
	if( sfield(font_list) == NULL )
	{
	    sfield(font_list) = XmeGetDefaultRenderTable(set, XmLABEL_FONTLIST);
	}
	sfield(font_list) = XmFontListCopy(sfield(font_list));
    }

    if( cfield(tab_side) != sfield(tab_side) ||
        cfield(tab_orientation) != sfield(tab_orientation) )
    {
	switch( sfield(tab_side) )
	{
	case XmTABS_ON_TOP:
	default:
	    XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
	    XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_BOTTOM_RIGHT); ++n;
	    if( sfield(tab_orientation) == XmTAB_ORIENTATION_DYNAMIC )
	    {
		XtSetArg(args[n], XmNtabOrientation, XmTABS_LEFT_TO_RIGHT);
	    }
	    else
	    {
		XtSetArg(args[n], XmNtabOrientation, sfield(tab_orientation));
	    }
	    n++;
	    break;
	case XmTABS_ON_BOTTOM:
	    XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
	    XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_TOP_LEFT); ++n;
	    if( sfield(tab_orientation) == XmTAB_ORIENTATION_DYNAMIC )
	    {
		XtSetArg(args[n], XmNtabOrientation, XmTABS_LEFT_TO_RIGHT);
	    }
	    else
	    {
		XtSetArg(args[n], XmNtabOrientation, sfield(tab_orientation));
	    }
	    n++;
	    break;
	case XmTABS_ON_RIGHT:
	    XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
	    XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_TOP_LEFT); ++n;
	    if( sfield(tab_orientation) == XmTAB_ORIENTATION_DYNAMIC )
	    {
		XtSetArg(args[n], XmNtabOrientation, XmTABS_TOP_TO_BOTTOM);
	    }
	    else
	    {
		XtSetArg(args[n], XmNtabOrientation, sfield(tab_orientation));
	    }
	    n++;
	    break;
	case XmTABS_ON_LEFT:
	    XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
	    XtSetArg(args[n], XmNtabEdge, XmTAB_EDGE_BOTTOM_RIGHT); ++n;
	    if( sfield(tab_orientation) == XmTAB_ORIENTATION_DYNAMIC )
	    {
		XtSetArg(args[n], XmNtabOrientation, XmTABS_BOTTOM_TO_TOP);
	    }
	    else
	    {
		XtSetArg(args[n], XmNtabOrientation, sfield(tab_orientation));
	    }
	    n++;
	    break;
	}
	need_layout = True;
	need_resize = True;
	need_redraw = True;
    }

    if( !sfield(uniform_tab_size) &&
        sfield(uniform_tab_size) != cfield(uniform_tab_size) &&
        (XmTabStack_tab_mode(s_tab) == XmTABS_STACKED ||
	 XmTabStack_tab_mode(s_tab) == XmTABS_STACKED_STATIC) )
    {
	XmeWarning(set, XmNillegalUniformTabSizeMsg);
	XtSetArg(args[n], XmNuniformTabSize, True); ++n;
    }

    if( (sfield(tab_mode) == XmTABS_STACKED ||
	 sfield(tab_mode) == XmTABS_STACKED_STATIC) &&
        sfield(tab_mode) != cfield(tab_mode) &&
        !sfield(uniform_tab_size) )
    {
	XmeWarning(set, XmNillegalUniformTabSizeMsg);
	XtSetArg(args[n], XmNuniformTabSize, True); ++n;
    }
	 
    if( XmTabStack__inited(s_tab) && (XmTabStack_tab_box(s_tab) != NULL ))
    {
	_XmFilterArgs(arg_list, *arg_cnt, tab_stack_filter,
		      &filtered_args, &num_filtered_args);
	merged_args = XtMergeArgLists(filtered_args, num_filtered_args,
				      args, n);
	XtSetValues(XmTabStack_tab_box(s_tab), merged_args,
		    n + num_filtered_args); n = 0;
	XtFree((XtPointer)filtered_args);
	XtFree((XtPointer)merged_args);
    }

#ifdef TEAR_OFF_TABS
    if( cfield(tear_off_label) != sfield(tear_off_label) )
    {
	if( cfield(tear_off_label) != NULL )
	{
	    XmStringFree(cfield(tear_off_label));
	}
	
	if( sfield(tear_off_label) != NULL )
	{
	    sfield(tear_off_label) = XmStringCopy(sfield(tear_off_label));
	}
	XtVaSetValues(sfield(_tear_off_button),
		      XmNlabelString, sfield(tear_off_label),
		      NULL);
    }
#endif /* TEAR_OFF_TABS */

    if( need_layout )
    {
	Resize(set);
    }

    if( need_redraw )
    {
	Widget canvas = _XmTabBoxCanvas(XmTabStack_tab_box(s_tab));

	if( XtIsRealized(canvas) )
	{
	    XClearArea(XtDisplay(set), XtWindow(canvas), 0, 0, 0, 0, True);
	}
    }

    if( need_resize )
    {
	XtWidgetGeometry unused, allowed;

	unused.request_mode = 0;
	QueryGeometry(set, &unused, &allowed);

	if( BBPart(s_tab).resize_policy != XmRESIZE_NONE )
	{

	    /*
	     * Lets just adjust the values depending on our resize
	     * policy.
	     */
	    if( BBPart(s_tab).resize_policy == XmRESIZE_GROW )
	    {
		if( allowed.width < s_tab->core.width )
		{
		    allowed.width = s_tab->core.width;
		}
		if( allowed.height < s_tab->core.height )
		{
		    allowed.height = s_tab->core.height;
		}
	    }
	    
	    if( XtWidth(request) == XtWidth(current) )
	        
	    {
		s_tab->core.width = allowed.width;
	    }
	    if( XtHeight(request) == XtHeight(current) )
	    {
		s_tab->core.height = allowed.height;
	    }
	}
    }

    return( need_redraw || need_layout || need_resize );
}
#undef cfield
#undef rfield
#undef sfield
#undef bcfield
#undef bsfield

static XtGeometryResult
#ifndef _NO_PROTO
QueryGeometry(Widget widget, XtWidgetGeometry *request,
	      XtWidgetGeometry *allowed)
#else
QueryGeometry(widget, request, allowed)
    Widget           widget;
    XtWidgetGeometry *request, *allowed;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget)widget;
    WidgetList       kid;
    XtWidgetGeometry tab_wanted;
    Cardinal         i;
    Boolean	     have_width, have_height;
    int              kid_width = 0, kid_height = 0, width, height;

    /*
     * First lets check to see if the caller is interested in the
     * same things we are, i.e. if the caller does not care about
     * the width or height then we will accept whatever they
     * give us.
     */
    if( request->request_mode != 0 &&
        !(request->request_mode & (CWWidth | CWHeight)) )
    {
	return( XtGeometryYes );
    }

    *allowed = *request;
    allowed->request_mode = CWWidth | CWHeight;

    /*
     * If we get this far we know that they care about our Dimensions
     * so lets grab the desired width/height of our kids and see what
     * they want.  We calculate our kids width by taking the max of
     * all the widths and heights and then adding room for the margins.
     */
    for( i = 0, kid = tab->composite.children;
	 i < tab->composite.num_children; ++i )
    {
	if( !IsValidChild(tab, kid[i]) ) continue;

	AssignMax(kid_width, (int)XmTabStackC_width(kid[i]));
	AssignMax(kid_height, (int)XmTabStackC_height(kid[i]));
    }

    /*
     * Now that we know the bounding box of all the children lets add the
     * margin to that area.
     */
    width = kid_width += 2 * BBPart(tab).margin_width +
			      tab->manager.shadow_thickness;
    
    height = kid_height += 2 * BBPart(tab).margin_height +
				tab->manager.shadow_thickness;
    if (XmTabStack_tab_side(tab) == XmTABS_ON_TOP
            || XmTabStack_tab_side(tab) == XmTABS_ON_BOTTOM)
    {
        width += tab->manager.shadow_thickness;
    } else /* if (XmTabStack_tab_side(tab) == XmTABS_ON_LEFT
            || XmTabStack_tab_side(tab) == XmTABS_ON_RIGHT) */
    {
        height += tab->manager.shadow_thickness;
    }

    if( LocalTabBox(tab) )
    {
	/*
	 * Now lets find out what size the TabBox wants to be.
	 */
	if( XmTabStack_tab_mode(tab) == XmTABS_STACKED ||
	   XmTabStack_tab_mode(tab) == XmTABS_STACKED_STATIC )
	{
	    XRectangle tab_rect;

	    switch( XmTabStack_tab_side(tab) )
	    {
	    case XmTABS_ON_TOP:
	    case XmTABS_ON_BOTTOM:
		_XmTabBoxStackedGeometry(XmTabStack_tab_box(tab), width,
					 &tab_rect);
		break;
	    case XmTABS_ON_RIGHT:
	    case XmTABS_ON_LEFT:
		_XmTabBoxStackedGeometry(XmTabStack_tab_box(tab), height,
					 &tab_rect);
		break;
	    }
	    tab_wanted.width = tab_rect.width;
	    tab_wanted.height = tab_rect.height;
	}
	else
	{
	    XtQueryGeometry(XmTabStack_tab_box(tab), NULL, &tab_wanted);
	}
	
	
	/*
	 * Now we need to look at the placement of the TabBox and
	 * determine how we should merge its desired size in with the
	 * size of the children.
	 */
	switch( XmTabStack_tab_side(tab) )
	{
	case XmTABS_ON_TOP:
	case XmTABS_ON_BOTTOM:
	default:
	    /*
	     * For these cases we need to take the max of the width of the
	     * children and the TabBox and then add the TabBox's height
	     * to the children's.
	     */
	    AssignMax(width, (int)tab_wanted.width);
	    height += tab_wanted.height;
	    break;
	case XmTABS_ON_RIGHT:
	case XmTABS_ON_LEFT:
	    /*
	     * For these cases we need to take the max of the height of the
	     * children and the TabBox and then add the TabBox's width
	     * to the children's.
	     */
	    width += tab_wanted.width;
	    AssignMax(height, (int)tab_wanted.height);
	    break;
	}
    }

    /*
     * Lets first check the simple case.  This is the case where the
     * caller is interested in the geometry that we would like to 
     * be.
     */
    if( request->request_mode == 0 )
    {
	/*
	 * Well it seems that the caller is just interested in the
	 * size that we want to be, so lets stuff that away to our 
	 * return structure and send that back.
	 */
	allowed->width = width;
	allowed->height = height;

	/*
	 * Before we give a result lets see if the geometry that
	 * we would give back is our current geometry and if it is
	 * we will return XtGeometryNo to indicate that we want to
	 * stay the way we are.
	 */
	if( XmCompareXtWidgetGeometryToWidget(allowed, widget) )
	{
	    return( XtGeometryNo );
	}

	return( XtGeometryYes );
    }

    /*
     * If we got here that means that the caller wants to specify
     * either our width or height and see how we would react to such
     * a change.  So lets what they want.
     */
    have_width = ((request->request_mode & CWWidth) != 0);
    have_height = ((request->request_mode & CWHeight) != 0);

    if( have_width && have_height )
    {
	/*
	 * It seems that the caller wants to specify our with and
	 * height.  If this is the case then we want to tell them that
	 * we prefer our desired geometry.
	 */
	allowed->width = width;
	allowed->height = height;
	if( XmCompareXtWidgetGeometryToWidget(allowed, widget) )
	{
	    return( XtGeometryNo );
	}

	if( XmCompareXtWidgetGeometry(request, allowed) )
	{
	    return( XtGeometryYes );
	}

	return( XtGeometryAlmost );
    }

    /*
     * Finally the hard part.  Here it seems that the caller wants to 
     * freeze either our width or height and have us adjust the other
     * dimension. 
     */
    if( have_width )
    {
	/*
	 * Ok it seems that the caller wants to freeze our width so lets
	 * find out what we can do about this.  This case is only interesting
	 * if the tabs are on the top or the bottom.  Because if they are
	 * on the sides this case diverts to the we want our preferred geomtry
	 * case because we don't want to shrink our kids below their 
	 * preferred size and we don't want to shrink the tabs height.
	 */
	switch( XmTabStack_tab_side(tab) )
	{
	case XmTABS_ON_RIGHT:
	case XmTABS_ON_LEFT:
	    /*
	     * This is the boring case and degrades to the "we want our
	     * preferred geometry" case.
	     */
	    allowed->width = width;
	    allowed->height = height;
	    break;
	case XmTABS_ON_TOP:
	case XmTABS_ON_BOTTOM:
	default:
	    /*
	     * Here is the interesting case.  The uses has frozen our width
	     * but not our height.  Now as long as the width they specified
	     * is greater than the width of the kids we are in business, else
	     * we degrade to give me what I want.
	     */
	    if( (int)request->width < kid_width )
	    {
		/*
		 * The degraded case: "Give me what I want"
		 */
		allowed->width = width;
		allowed->height = height;
	    }
	    else
	    {
		XtWidgetGeometry ask, got;
		/*
		 * We actually get to have some fun now.  What we now
		 * do is ask the TabBox what it is willing to do
		 * "heightwise" if we freeze its width.
		 */
		ask.request_mode = CWWidth;
		ask.width = request->width;
		got.request_mode = 0;
		switch( XtQueryGeometry(XmTabStack_tab_box(tab), &ask, &got) )
		{
		case XtGeometryYes:
		case XtGeometryAlmost:
		default:
		    if( !(got.request_mode & CWWidth) )
		    {
			got.width = tab_wanted.width;
		    }
		    if( !(got.request_mode & CWHeight) )
		    {
			got.height = tab_wanted.height;
		    }

		    /*
		     * Now we have the height that the TabBox is willing
		     * to take if we freeze its width so lets
		     * store this information back into our return
		     * structure.
		     */
		    allowed->width = Max(kid_width, (int)got.width);
		    allowed->height = kid_height + got.height;
		    break;
		case XtGeometryNo:
		    allowed->width = width;
		    allowed->height = height;
		    break;
		}
	    }
	    break;
	}
    }
    else if( have_height )
    {
	/*
	 * It seems that the caller want to freeze our height and see how
	 * we react "widthwise".  This case degrades to the default if
	 * the tabs are on the right or left.
	 */
	switch( XmTabStack_tab_side(tab) )
	{
	case XmTABS_ON_TOP:
	case XmTABS_ON_BOTTOM:
	default:
	    allowed->width = width;
	    allowed->height = height;
	    break;
	case XmTABS_ON_RIGHT:
	case XmTABS_ON_LEFT:

	    /*
	     * First we will do a quick check to make sure that the height
	     * they are asking about will fit all the children at least, and
	     * if not we will reject it out of hand.
	     */
	    if( (int)request->height < kid_height )
	    {
		allowed->width = width;
		allowed->height = height;
	    }
	    else
	    {
		XtWidgetGeometry ask, got;

		/*
		 * Now letws see how the TabBox reacts to the new size.
		 */
		ask.request_mode = CWHeight;
		ask.request_mode = request->height;
		got.request_mode = 0;
		switch( XtQueryGeometry(XmTabStack_tab_box(tab), &ask, &got) )
		{
		case XtGeometryYes:
		case XtGeometryAlmost:
		default:
		    if( !(got.request_mode & CWWidth) )
		    {
			got.width = tab_wanted.width;
		    }
		    if( !(got.request_mode & CWHeight) )
		    {
			got.height = tab_wanted.height;
		    }
		    
		    /*
		     * Now we have the info we need so lets stuff that back
		     * into our return structure.
		     */
		    allowed->width = kid_width + got.width;
		    allowed->height = Max(kid_height, (int)got.height);
		    break;
		case XtGeometryNo:
		    allowed->width = width;
		    allowed->height = height;
		    break;
		}
	    }
	    break;
	}
    }

    if( XmCompareXtWidgetGeometryToWidget(allowed, widget) )
    {
	return( XtGeometryNo );
    }
    else if( XmCompareXtWidgetGeometry(request, allowed) )
    {
	return( XtGeometryYes );
    }
     
    return( XtGeometryNo );
}

#if 0
#define XiReturn(i,r) \
    printf("%d: RESULT: XtGeometry", i); \
    switch( r ) \
    { \
    case XtGeometryYes: \
    default: \
	printf("Yes"); \
	break; \
    case XtGeometryNo: \
	printf("No"); \
	break; \
    case XtGeometryAlmost: \
	printf("Almost"); \
	break; \
    case XtGeometryDone: \
	printf("Done"); \
	break; \
    } \
    if( request->request_mode & XtCWQueryOnly ) printf("(QUERY)\n"); \
    else printf("\n"); \
    printf("  WIDGET: %s\n", XtName(widget)); \
    printf("  X      : "); \
    if( allowed->request_mode & CWX ) printf("%d\n", allowed->x); \
    else printf("N/A\n"); \
    printf("  Y      : "); \
    if( allowed->request_mode & CWY ) printf("%d\n", allowed->y); \
    else printf("N/A\n"); \
    printf("  WIDTH  : "); \
    if( allowed->request_mode & CWWidth ) printf("%d\n", allowed->width); \
    else printf("N/A\n"); \
    printf("  HEIGHT : "); \
    if( allowed->request_mode & CWHeight ) printf("%d\n", allowed->height); \
    else printf("N/A\n"); \
    printf("  BORDER : "); \
    if( allowed->request_mode & CWBorderWidth ) \
	printf("%d\n", allowed->border_width); \
    else printf("N/A\n"); \
    printf("\n"); \
    return(r);
#else
#define XiReturn(i,r) return( r )
#endif

static XtGeometryResult
#ifndef _NO_PROTO
GeometryNo(XmTabStackWidget tab, Widget widget, XtWidgetGeometry *request,
	   XtWidgetGeometry *allowed)
#else
GeometryNo(tab, widget, request, allowed)
    XmTabStackWidget tab;
    Widget           widget;
    XtWidgetGeometry *request, *allowed;
#endif
{
    XRectangle box, kid;

    /*
     * If we are not dealing with the tab box child then we really want
     * to return No, so lets to that.
     */
    if( widget != XmTabStack_tab_box(tab) )
    {
	return( XtGeometryNo );
    }

    /*
     * If we got here that means that we know we are dealing with our
     * tab box child, and we are about to deny a size request from that
     * child.  The problem is that we do not want to do this. Actually
     * we want to resize the tab box child to fix in the space provided.
     */
    PickSizes(tab, XtWidth(tab), XtHeight(tab), &box, &kid);

    allowed->request_mode = CWWidth | CWHeight;
    allowed->width = box.width;
    allowed->height = box.height;

    if( XmCompareXtWidgetGeometryToWidget(allowed,
					  XmTabStack_tab_box(tab)) )
    {
	return( XtGeometryNo );
    }
    if( XmCompareXtWidgetGeometry(request, allowed) )
    {
	if( !(request->request_mode & XtCWQueryOnly) )
	{
	    Resize((Widget)tab);
	    if( XtIsRealized((Widget)tab) ) Redisplay((Widget)tab,NULL,False);
	}
	return( XtGeometryYes );
    }
    
    return( XtGeometryAlmost );
}

/*
 * Function:
 *	GeometryManager(widget, request, allowed)
 * Description:
 *	This function handles geometry requests. These requests are
 *	sent from the children.
 * Input:
 *	widget  : Widget            - the widget making the request.
 *	request : XtWidgetGeometry* - the size the child wants
 *	allowed : XtWidgetGeometry* - the size the child gets
 * Output:
 *	XtGeometryResult - specifies information about the allowed geometry
 *			   result.
 */
static XtGeometryResult
#ifndef _NO_PROTO
GeometryManager(Widget widget, XtWidgetGeometry *request,
		XtWidgetGeometry *allowed)
#else
GeometryManager(widget, request, allowed)
    Widget           widget;
    XtWidgetGeometry *request, *allowed;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) XtParent(widget);
    int              save_width, save_height, save_border;
    Dimension        child_save_width, child_save_height;
    XtWidgetGeometry want, got;
    XRectangle       box, kids;

#if 0
    printf("REQUEST: ");
    if( request->request_mode & XtCWQueryOnly ) printf("QUERY");
    printf("\n");
    printf("  WIDGET: %s\n", XtName(widget));
    printf("  X      : ");
    if( request->request_mode & CWX ) printf("%d\n", request->x);
    else printf("N/A\n");
    printf("  Y      : ");
    if( request->request_mode & CWY ) printf("%d\n", request->y);
    else printf("N/A\n");
    printf("  WIDTH  : ");
    if( request->request_mode & CWWidth ) printf("%d\n", request->width);
    else printf("N/A\n");
    printf("  HEIGHT : ");
    if( request->request_mode & CWHeight ) printf("%d\n", request->height);
    else printf("N/A\n");
    printf("  BORDER : ");
    if( request->request_mode & CWBorderWidth )
	printf("%d\n", request->border_width);
    else printf("N/A\n");
    printf("\n");
#endif

    if( XmTabStack__set_tab_list(tab) &&
        (request->request_mode & CWBorderWidth) )
    {
	XmTabStack__set_tab_list(tab) = False;
	XtVaSetValues(XmTabStack_tab_box(tab),
		      XmNtabList, XmTabStack__tab_list(tab),
		      NULL);
#ifdef TEAR_OFF_TABS
	BuildMenu(tab);
#endif
	XiReturn( 1, XtGeometryNo );
    }

    *allowed = *request;
    /*
     * Ok playing with geometry is real easy.  This is because we only
     * let our children change their dimensions and not their location.
     * So if the caller wants to muck with their X or Y we ignore it.
     *
     * Also stacking order does not matter because only one child is 
     * visible at a time.
     */
    allowed->request_mode &= ~(CWX | CWY | CWStackMode | CWSibling);

    /*
     * If the child wants does not want to change its dimensions (width,
     * height or border width) then we are done.
     */
    if( !(allowed->request_mode & (CWWidth | CWHeight | CWBorderWidth)) )
    {
	/*
	 * It turns out that we have weeded out everything from their
	 * request which means that we are not excepting any of their
	 * changes and we can just return XtGeometryNo.
	 */
	XiReturn( 2, XtGeometryNo );
    }

    /*
     * Lets find out what size we want to be if we accept this kids
     * request.
     */
    save_width = XtWidth(widget);
    save_height = XtHeight(widget);
    save_border = widget->core.border_width;
    if( allowed->request_mode & CWWidth )
    {
	widget->core.width = allowed->width;
	XmTabStackC_width(widget) = allowed->width;
    }
    if( allowed->request_mode & CWHeight )
    {
	widget->core.height = allowed->height;
        XmTabStackC_height(widget) = allowed->height;
    }
    if( allowed->request_mode & CWBorderWidth )
    {
	widget->core.border_width = allowed->border_width;
    }

    child_save_width = widget->core.width;
    child_save_height = widget->core.height;

    want.request_mode = 0;
    QueryGeometry((Widget)tab, &want, &got);
    widget->core.width = save_width;
    widget->core.height = save_height;
    widget->core.border_width = save_border;

    XmTabStackC_width(widget) = save_width;
    XmTabStackC_height(widget) = save_height;

    /*
     * Lets store away the width and height we want.
     */
    save_width = XtWidth(tab);
    save_height = XtHeight(tab);
    if( got.request_mode & CWWidth )
    {
	save_width = got.width;
    }
    if( got.request_mode & CWHeight )
    {
	save_height = got.height;
    }

    /*
     * Lets first check to see if we can satify this request without
     * changing our size and if so we will just return yes.  We will
     * also use this chance to check our resize policy.
     */
#ifdef DKB
    if( save_width == XtWidth(tab) && save_height == XtHeight(tab) )
    {
	/*
	 * We can satify this request without changing our size so
	 * lets retrun XtGeometryNo, to let the kid know it should
	 * stay the same size.
	 */
	XiReturn( 3, GeometryNo(tab, widget, request, allowed) );
    }
#endif

    /*
     * If we got here that means that the our child wants us to change
     * our size, so lets see if our resize polify will allow this
     * chnage.
     */
    if( BBPart(tab).resize_policy == XmRESIZE_NONE ||
        (BBPart(tab).resize_policy == XmRESIZE_GROW &&
	 (save_width < (int)XtWidth(tab) ||save_height < (int)XtHeight(tab))) )
    {
	/*
	 * Well we cannot allow this change so what we want is our child
	 * to stay the same size it is, so lets return XtGeometryNo.
	 */
	XiReturn( 4, GeometryNo(tab, widget, request, allowed) );
    }

    /*
     * Now we know what size we want to be if we accept this kids request
     * so now lets if we can be this size.
     */
    want.request_mode = CWWidth | CWHeight | XtCWQueryOnly;
    want.width = save_width;
    want.height = save_height;

    switch( XtMakeGeometryRequest((Widget)tab, &want, &got) )
    {
    case XtGeometryYes:
    default:
	/*
	 * Well our parent said yes, so we can accept the request from
	 * our kid, so what we will do is now check if they really want
	 * us to do it or if this was a query.
	 */

	if( (request->request_mode & XtCWQueryOnly) == 0 )
	{
	    /*
	     * This was not a query so we now really want to make the
	     * request to our parent.  We don't care about the return
	     * value from this call because the parent already said that
	     * they would accept this.
	     */
	    XtMakeResizeRequest((Widget)tab, save_width, save_height,
				NULL, NULL);

	    XmTabStackC_width(widget) = child_save_width;
	    XmTabStackC_height(widget) = child_save_height;

	    Resize((Widget)tab);
	    if( XtIsRealized((Widget)tab) ) Redisplay((Widget)tab,NULL,False);
	}
	XiReturn( 5, XtGeometryYes );
    case XtGeometryNo:
	/*
	 * Our parent said no, so we must also say no.
	 */
	XiReturn( 6, GeometryNo(tab, widget, request, allowed) );
    case XtGeometryAlmost:
	/*
	 * Out parent said almost, so we need to figure out how this
	 * affects our children, i.e. what is our children's dimensions
	 * with this change.
	 */
	break;
    case XtGeometryDone:
	XmTabStackC_width(widget) = child_save_width;
	XmTabStackC_height(widget) = child_save_height;

	XiReturn( 99, XtGeometryDone );
	break;
    }

    /*
     * This is the almost case. To get here we have had to ask our parent for
     * a size change, and our parent said we could not be the size that
     * we want to be, but we could be a "close" size. So what we have to 
     * do is figure out how this affects our children and return the 
     * proper response.
     */
    
    /*
     * First lets see if we can allow the size change that our parent
     * has said OK to.  This means that we have fake a layout to find
     * out what size our tabbox and children will be.
     */
    if( BBPart(tab).resize_policy == XmRESIZE_GROW &&
        (got.width < XtWidth(tab) || got.height < XtHeight(tab)) )
    {
	/*
	 * It turns out that our resize policy want allow us to make
	 * this compromise so lets just return no.
	 */
	XiReturn( 7, GeometryNo(tab, widget, request, allowed) );
    }

    /*
     * Ok we could accept this compromise, so lets see how it effects
     * the size of our kids.
     */
    PickSizes(tab, got.width, got.height, &box, &kids);

    allowed->request_mode |= (CWWidth | CWHeight);
    if( widget == XmTabStack_tab_box(tab) )
    {
	/*
	 * In this case we are dealing with our tab box child, so
	 * lets set the almost result to size we picked.
	 */
	allowed->width = box.width;
	allowed->height = box.height;
    }
    else
    {
	/*
	 * In this case we are dealing with our children, so lets
	 * set the almost return value.
	 */
	allowed->width = kids.width;
	allowed->height = kids.height;
    }

    XiReturn( 8, XtGeometryAlmost );
}

static void
#ifndef _NO_PROTO
ChangeManaged(Widget widget)
#else
ChangeManaged(widget)
    Widget widget;
#endif
{
    XmTabStackWidget  tab = (XmTabStackWidget) widget;
    WidgetList        kid;
    Widget	      active;
    int		      idx = 0;
    Cardinal          i;
    XmTabbedStackList         tl = NULL;
    XmTabAttributeRec attr;
    XtWidgetGeometry  request, geom;
    XmString          xmstr;
    Boolean           changed_size = False;
    Boolean           select_new_kid = False;

    if( !XmTabStack__inited(tab) ) return;

    /*
     * Lets first find out if our active child is still managed, and
     * if it is not then lets NULLify our active child.
     */
    if( XmTabStack__active_child(tab) != NULL &&
        !XtIsManaged(XmTabStack__active_child(tab)) )
    {
	XmTabStack__active_child(tab) = (Widget) NULL;
    }

    /*
     * If we do not have an active child then lets find the first
     * managed child and make it active.  We will walk the whole list
     * of children and make sure that the active child is the only
     * child that is visible.
     */
    for( i = 0, kid = tab->composite.children;
	 i < tab->composite.num_children; ++i, ++kid )
    {
	if( IsTabBox(tab, *kid) ) continue;

	if( !XtIsManaged(*kid) )
	{
	    /*
	     * If this child is not managed then lets zero out
	     * the widgets managed width and height so that 
	     * when it becomes managed again we will grab the
	     * new managed width/height.
	     */
	    XmTabStackC_width(*kid) = 0;
	    XmTabStackC_height(*kid) = 0;
	    XmTabStackC_index(*kid) = -1;
	    continue;
	}

	XmTabStackC_index(*kid) = idx++;

	if( XmTabStack__active_child(tab) == NULL )
	{
	    select_new_kid = True;

	    XmTabStack__active_child(tab) = *kid;
	}

	/*
	 * Now that we know that we are dealing with a managed child
	 * lets add them to the tab list we are building up to 
	 * pass off to the TabBox.
	 */
	if( tl == NULL )
	{
	    tl = XmTabbedStackListCreate();
	}
	
	if( XmTabStackC_tab_label_string(*kid) == NULL )
	{
	    xmstr = attr.label_string = XmStringCreateLocalized(XtName(*kid));
	    attr.value_mode = XmTAB_VALUE_COPY;
	}
	else
	{
	    xmstr = NULL;
	    attr.label_string = XmTabStackC_tab_label_string(*kid);
	    attr.value_mode = XmTAB_VALUE_SHARE;
	}
	attr.string_direction = XmTabStackC_tab_string_direction(*kid);
	attr.label_pixmap = XmTabStackC_tab_label_pixmap(*kid);
	attr.label_alignment = XmTabStackC_tab_alignment(*kid);
	attr.pixmap_placement = XmTabStackC_tab_pixmap_placement(*kid);
	attr.foreground = XmTabStackC_tab_foreground(*kid);
	attr.background = XmTabStackC_tab_background(*kid);
	attr.background_pixmap = XmTabStackC_tab_background_pixmap(*kid);
	attr.sensitive = XtIsSensitive(*kid);
	XmTabbedStackListAppend(tl, XmTAB_ALL_FLAGS, &attr);
	if( xmstr != NULL ) XmStringFree(xmstr);

	/*
	 * Next lets check if this kid has its managed width and
	 * height set and if not lets set it.
	 */
	if( XmTabStackC_width(*kid) == 0 )
	{
	    XmTabStackC_width(*kid) = XtWidth(*kid);
	    XmTabStackC_height(*kid) = XtHeight(*kid);
	}
    }

    if( (active = XmTabStack__active_child(tab)) != NULL )
    {
	XtVaSetValues(XmTabStack_tab_box(tab),
		      XmNtabList, tl,
		      XmNselectedIndex, XmTabStackC_index(active),
		      XmNtraversalIndex, XmTabStackC_index(active),
		      NULL);
    }
    else
    {
	XtVaSetValues(XmTabStack_tab_box(tab),
		      XmNtabList, tl,
		      NULL);
    }
    if( XmTabStack__tab_list(tab) != NULL )
    {
	XmTabbedStackListFree(XmTabStack__tab_list(tab));
    }
    XmTabStack__tab_list(tab) = tl;

#ifdef TEAR_OFF_TABS
    BuildMenu(tab);
#endif

    /*
     * Now lets see how this new kid affects our geometry and request
     * a new size.
     */
    request.request_mode = 0;
    QueryGeometry((Widget)tab, &request, &geom);

    /*
     * Now that we know the size that we want to be lets see if we are 
     * that size and if not lets request a new size.  But before we do this
     * lets check our resize policy to see if we want to change size.
     */
    if( (geom.width != XtWidth(tab) || geom.height != XtHeight(tab)) &&
        BBPart(tab).resize_policy != XmRESIZE_NONE )
    {
	Dimension width, height;

	if( BBPart(tab).resize_policy != XmRESIZE_GROW ||
	    (geom.width >= XtWidth(tab) && geom.height >= XtHeight(tab)) )
	{
	    switch( XtMakeResizeRequest((Widget)tab, geom.width, geom.height,
					&width, &height) )
	    {
	    case XtGeometryYes:
		changed_size = True;
	    case XtGeometryNo:
	    default:
		break;
	    case XtGeometryAlmost:
		/*
		 * Our parent gave us an alternate suggestion so lets do what
		 * mom or dad says and take it.
		 */
		if( BBPart(tab).resize_policy != XmRESIZE_GROW ||
		    (width >= XtWidth(tab) && height >= XtHeight(tab)) )
		{
		    XtMakeResizeRequest((Widget)tab, width, height, NULL,
					NULL);
		    changed_size = True;
		}
		break;
	    }
	}
    }
    Resize((Widget)tab);
    if( !changed_size && XtIsRealized((Widget)tab) )
    {
	Redisplay((Widget)tab, NULL, False);
    }

    /*
     * If new tab is selected as a result of a manage set changed, then notify
     * the next tab stack widget through callbacks that it is now selected...
     */
    if (select_new_kid)
    {
	XmTabStackCallbackStruct	cbdata;

	cbdata.reason = XmCR_TAB_SELECTED;
	cbdata.event = NULL;
	cbdata.selected_child = XmTabStack__active_child(tab);

	XtCallCallbackList((Widget)tab, XmTabStack_tab_select_callback(tab),
			   (XtPointer) &cbdata);
    }
}

/* ARGSUSED */
static void
#ifndef _NO_PROTO
ConstraintInitialize(Widget request, Widget set, ArgList arg_list,
		     Cardinal *arg_cnt)
#else
ConstraintInitialize(request, set, arg_list, arg_cnt)
    Widget   request, set;
    ArgList  arg_list;
    Cardinal *arg_cnt;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) XtParent(set);

    if( XmTabStackC_tab_label_string(set) != NULL )
    {
	XmTabStackC_tab_label_string(set) = XmStringCopy(XmTabStackC_tab_label_string(set));
    }

    if( XmTabStackC_tab_string_direction(set) == XmSTRING_DIRECTION_DEFAULT )
    {
	XmTabStackC_tab_string_direction(set) = tab->manager.string_direction;
    }
			  
    XmTabStackC_width(set) = 0;
    XmTabStackC_height(set) = 0;
    XmTabStackC_index(set) = -1;
}

static void
#ifndef _NO_PROTO
ConstraintDestroy(Widget widget)
#else
ConstraintDestroy(widget)
    Widget widget;
#endif
{
    if( XmTabStackC_tab_label_string(widget) != NULL )
    {
	XmStringFree(XmTabStackC_tab_label_string(widget));
    }
    if( XmTabStackC_free_tab_pixmap(widget) && ValidPixmap(XmTabStackC_tab_label_pixmap(widget)) )
    {
	XFreePixmap(XtDisplay(widget), XmTabStackC_tab_label_pixmap(widget));
    }
}

#define cfield(f) (XmTabStackC_##f(current))
#define rfield(f) (XmTabStackC_##f(request))
#define sfield(f) (XmTabStackC_##f(set))
/* ARGSUSED */
static Boolean
#ifndef _NO_PROTO
ConstraintSetValues(Widget current, Widget request, Widget set,
		    ArgList arg_list, Cardinal *arg_cnt)
#else
ConstraintSetValues(current, request, set, arg_list, arg_cnt)
    Widget   current, request, set;
    ArgList  arg_list;
    Cardinal *arg_cnt;
#endif
{
    XmTabStackWidget  tab = (XmTabStackWidget) XtParent(set);
    XmTabAttributeRec attr;
    WidgetList        kid;
    Cardinal          i;

    /*
     * If any of the following have changed that means we need to 
     * build a new TabList and send it to our child.
     */
    if( cfield(tab_label_string) != sfield(tab_label_string) ||
        cfield(tab_alignment) != sfield(tab_alignment) ||
        cfield(tab_string_direction ) != sfield(tab_string_direction) ||
        cfield(tab_label_pixmap ) != sfield(tab_label_pixmap) ||
        cfield(tab_pixmap_placement ) != sfield(tab_pixmap_placement) ||
        cfield(tab_background ) != sfield(tab_background) ||
        cfield(tab_background_pixmap) != sfield(tab_background_pixmap) ||
        cfield(tab_foreground ) != sfield(tab_foreground) ||
	XtIsSensitive(current) != XtIsSensitive(set) )
    {
	XmTabbedStackList tl = NULL;
	XmString  xmstr;

	if( cfield(tab_label_string) != sfield(tab_label_string) )
	{
	    if( cfield(tab_label_string) != NULL )
	    {
		XmStringFree(cfield(tab_label_string));
	    }
	    
	    sfield(tab_label_string) = XmStringCopy(sfield(tab_label_string));
	}

	for( i = 0, kid = tab->composite.children;
	     i < tab->composite.num_children; ++i, ++kid )
	{
	    if( !IsValidChild(tab, *kid) ) continue;

	    if( tl == NULL ) tl = XmTabbedStackListCreate();

	    if( XmTabStackC_tab_label_string(*kid) == NULL )
	    {
		xmstr = attr.label_string =
		    XmStringCreateLocalized(XtName(*kid));
		attr.value_mode = XmTAB_VALUE_COPY;
	    }
	    else
	    {
		xmstr = NULL;
		attr.label_string = XmTabStackC_tab_label_string(*kid);
		attr.value_mode = XmTAB_VALUE_SHARE;
	    }
	    attr.string_direction = XmTabStackC_tab_string_direction(*kid);
	    attr.label_pixmap = XmTabStackC_tab_label_pixmap(*kid);
	    attr.label_alignment = XmTabStackC_tab_alignment(*kid);
	    attr.pixmap_placement = XmTabStackC_tab_pixmap_placement(*kid);
	    attr.foreground = XmTabStackC_tab_foreground(*kid);
	    attr.background = XmTabStackC_tab_background(*kid);
	    attr.background_pixmap = XmTabStackC_tab_background_pixmap(*kid);
	    attr.sensitive = XtIsSensitive(*kid);
	    XmTabbedStackListAppend(tl, XmTAB_ALL_FLAGS, &attr);
	    if( xmstr != NULL ) XmStringFree(xmstr);
	}

	if( XmTabStack__tab_list(tab) != NULL )
	{
	    XmTabbedStackListFree(XmTabStack__tab_list(tab));
	}
	XmTabStack__tab_list(tab) = tl;

	if( XtIsManaged(set) && XtIsRealized(set) )
	{
	    set->core.border_width++;
	    XmTabStack__set_tab_list(tab) = True;
	}
    }

    /*
     * If the background color for our tab changes and this is the
     * active tab then we need cause a refresh to happen in the stack.
     */
    if( (cfield(tab_background ) != sfield(tab_background) ||
	 cfield(tab_background_pixmap) != sfield(tab_background_pixmap)) &&
         XtIsRealized((Widget)tab) )
    {
	XClearArea(XtDisplay(tab), XtWindow(tab), 0, 0, 0, 0, True);
    }

    return( False );
}
#undef cfield
#undef rfield
#undef sfield

#define XiCvtDone(type, value) 				\
    {							\
	if( to->addr != NULL )				\
	{						\
	    if( to->size < sizeof(type) )		\
	    {						\
		to->size = sizeof(type);		\
		return( False );			\
	    }						\
	    *(type*)(to->addr) = (value);		\
	}						\
	else						\
	{						\
	    static type static_val;			\
	    static_val = (value);			\
	    to->addr = (XtPointer) &static_val;		\
	}						\
	to->size = sizeof(type);			\
	return( True );					\
    }

/* ARGSUSED */
static Boolean
#ifndef _NO_PROTO
CvtStringToXiPixel(Display *dpy, XrmValue *arg_list, Cardinal *arg_cnt,
		   XrmValue *from, XrmValue *to, XtPointer *data)
#else
CvtStringToXiPixel(dpy, arg_list, arg_cnt, from, to, data)
    Display   *dpy;
    XrmValue  *arg_list;
    Cardinal  *arg_cnt;
    XrmValue  *from, *to;
    XtPointer *data;
#endif
{
    static Pixel result = XmCOLOR_DYNAMIC;
    String	 str = (String) from->addr;
    Widget       widget;

    widget = *((Widget*) arg_list[0].addr);

    if( XmCompareISOLatin1(str, "color_dynamic") == 0 ||
        XmCompareISOLatin1(str, "dynamic") == 0 )
    {
	XiCvtDone(Pixel, result);
    }

    return( XtConvertAndStore(widget, XmRString, from, XmRPixel, to) );
}

/* ARGSUSED */
static Boolean
#ifndef _NO_PROTO
CvtStringToXiPixmap(Display *dpy, XrmValue *arg_list, Cardinal *arg_cnt,
		    XrmValue *from, XrmValue *to, XtPointer *data)
#else
CvtStringToXiPixmap(dpy, arg_list, arg_cnt, from, to, data)
    Display   *dpy;
    XrmValue  *arg_list;
    Cardinal  *arg_cnt;
    XrmValue  *from, *to;
    XtPointer *data;
#endif
{
    static Pixmap result = XmPIXMAP_DYNAMIC;
    String	  str = (String) from->addr;
    Widget        widget;

    widget = *((Widget*) arg_list[0].addr);

    if( XmCompareISOLatin1(str, "pixmap_dynamic") == 0 ||
        XmCompareISOLatin1(str, "dynamic") == 0 )
    {
	XiCvtDone(Pixel, result);
    }

    return( XtConvertAndStore(widget, XmRString, from, XmRPrimForegroundPixmap, to) );
}

/* ARGSUSED */
static Boolean
#ifndef _NO_PROTO
CvtStringToXmTabSide(Display *dpy, XrmValue *arg_list, Cardinal *arg_cnt,
		     XrmValue *from, XrmValue *to, XtPointer *data)
#else
CvtStringToXmTabSide(dpy, arg_list, arg_cnt, from, to, data)
    Display   *dpy;
    XrmValue  *arg_list;
    Cardinal  *arg_cnt;
    XrmValue  *from, *to;
    XtPointer *data;
#endif
{
    static int result = XmTABS_ON_TOP;
    String     str = (String) (from->addr);

    if( XmCompareISOLatin1(str, "TOP") == 0 ||
        XmCompareISOLatin1(str, "XmTABS_ON_TOP") == 0 )
    {
	result = XmTABS_ON_TOP;
    }
    else if( XmCompareISOLatin1(str, "BOTTOM") == 0 ||
	     XmCompareISOLatin1(str, "XmTABS_ON_BOTTOM") == 0 )
    {
	result = XmTABS_ON_BOTTOM;
    }
    else if( XmCompareISOLatin1(str, "LEFT") == 0 ||
	     XmCompareISOLatin1(str, "XmTABS_ON_LEFT") == 0 )
    {
	result = XmTABS_ON_LEFT;
    }
    else if( XmCompareISOLatin1(str, "RIGHT") == 0 ||
	     XmCompareISOLatin1(str, "XmTABS_ON_RIGHT") == 0 )
    {
	result = XmTABS_ON_RIGHT;
    }
    else
    {
	XtDisplayStringConversionWarning(dpy, str, XmRXmTabSide);
	return( False );
    }

    XiCvtDone(int, result);
}

/* ARGSUSED */
static Boolean
#ifndef _NO_PROTO
CvtStringToXmPixmapPlacement(Display *dpy, XrmValue *arg_list,
			     Cardinal *arg_cnt, XrmValue *from, XrmValue *to,
			     XtPointer *data)
#else
CvtStringToXmPixmapPlacement(dpy, arg_list, arg_cnt, from, to, data)
    Display   *dpy;
    XrmValue  *arg_list;
    Cardinal  *arg_cnt;
    XrmValue  *from, *to;
    XtPointer *data;
#endif
{
    static XmPixmapPlacement result = XmPIXMAP_RIGHT;
    String                   str = (String) (from->addr);

    if( XmCompareISOLatin1(str, "TOP") == 0 ||
        XmCompareISOLatin1(str, "XmPIXMAP_TOP") == 0 )
    {
	result = XmPIXMAP_TOP;
    }
    else if( XmCompareISOLatin1(str, "BOTTOM") == 0 ||
	     XmCompareISOLatin1(str, "XmPIXMAP_BOTTOM") == 0 )
    {
	result = XmPIXMAP_BOTTOM;
    }
    else if( XmCompareISOLatin1(str, "RIGHT") == 0 ||
	     XmCompareISOLatin1(str, "XmPIXMAP_RIGHT") == 0 )
    {
	result = XmPIXMAP_RIGHT;
    }
    else if( XmCompareISOLatin1(str, "LEFT") == 0 ||
	     XmCompareISOLatin1(str, "XmPIXMAP_LEFT") == 0 )
    {
	result = XmPIXMAP_LEFT;
    }
    else if( XmCompareISOLatin1(str, "NONE") == 0 ||
	     XmCompareISOLatin1(str, "XmPIXMAP_NONE") == 0 )
    {
	result = XmPIXMAP_NONE;
    }
    else if( XmCompareISOLatin1(str, "ONLY") == 0 ||
	     XmCompareISOLatin1(str, "XmPIXMAP_ONLY") == 0 )
    {
	result = XmPIXMAP_ONLY;
    }
    else
    {
	XtDisplayStringConversionWarning(dpy, str, XmRXmPixmapPlacement);
	return( False );
    }
    
    XiCvtDone(XmPixmapPlacement, result);
}

static void
#ifndef _NO_PROTO
DrawShadows(XmTabStackWidget tab, GC top_GC, GC bottom_GC, int x, int y,
	    int width, int height)
#else
DrawShadows(tab, top_GC, bottom_GC, x, y, width, height)
    XmTabStackWidget tab;
    GC               top_GC, bottom_GC;
    int              x, y, width, height;
#endif
{
    XRectangle             top_rects[2], bottom_rects[2];
    XPoint                 bevel;
    int                    num_top_rects = 0, num_bottom_rects = 0,
                           shadow = tab->manager.shadow_thickness;
    /*
     * The only bit of redisplaying that we really have to do is draw
     * the three sides of the shadow that the XmTabBox does not draw.
     */

    switch( XmTabStack_tab_side(tab) )
    {
    case XmTABS_ON_TOP:
    default:
	top_rects[0].x = x;
	top_rects[0].y = y;
	top_rects[0].width = shadow;
	top_rects[0].height = height;
	num_top_rects = 1;

	bottom_rects[0].x = x;
	bottom_rects[0].y = y + height - shadow;
	bottom_rects[0].width = width;
	bottom_rects[0].height = shadow;

	bottom_rects[1].x = x + width - shadow;
	bottom_rects[1].y = y;
	bottom_rects[1].width = shadow;
	bottom_rects[1].height = height;
	num_bottom_rects = 2;

	bevel.x = x;
	bevel.y = y + height - shadow;
	break;
    case XmTABS_ON_BOTTOM:
	top_rects[0].x = x;
	top_rects[0].y = y;
	top_rects[0].width = shadow;
	top_rects[0].height = height;

	top_rects[1].x = x;
	top_rects[1].y = y;
	top_rects[1].width = width;
	top_rects[1].height = shadow;
	num_top_rects = 2;

	bottom_rects[0].x = x + width - shadow;
	bottom_rects[0].y = y;
	bottom_rects[0].width = shadow;
	bottom_rects[0].height = height;
	num_bottom_rects = 1;

	bevel.x = x + width - shadow;
	bevel.y = y;
	break;
    case XmTABS_ON_LEFT:
	top_rects[0].x = x;
	top_rects[0].y = y;
	top_rects[0].width = width;
	top_rects[0].height = shadow;
	num_top_rects = 1;

	bottom_rects[0].x = x;
	bottom_rects[0].y = y + height - shadow;
	bottom_rects[0].width = width;
	bottom_rects[0].height = shadow;

	bottom_rects[1].x = x + width - shadow;
	bottom_rects[1].y = y;
	bottom_rects[1].width = shadow;
	bottom_rects[1].height = height;
	num_bottom_rects = 2;

	bevel.x = x + width - shadow;
	bevel.y = y;
	break;
    case XmTABS_ON_RIGHT:
	top_rects[0].x = x;
	top_rects[0].y = y;
	top_rects[0].width = shadow;
	top_rects[0].height = height;

	top_rects[1].x = x;
	top_rects[1].y = y;
	top_rects[1].width = width;
	top_rects[1].height = shadow;
	num_top_rects = 2;

	bottom_rects[0].x = x;
	bottom_rects[0].y = y + height - shadow;
	bottom_rects[0].width = width;
	bottom_rects[0].height = shadow;
	num_bottom_rects = 1;

	bevel.x = x;
	bevel.y = y + height - shadow;
	break;
    }

    XFillRectangles(XtDisplay(tab), XtWindow(tab), top_GC, top_rects,
		    num_top_rects);
    XFillRectangles(XtDisplay(tab), XtWindow(tab), bottom_GC, bottom_rects,
		    num_bottom_rects);
    XmDrawBevel(XtDisplay(tab), XtWindow(tab), top_GC, bottom_GC,
		bevel.x, bevel.y, shadow, XmBEVEL_BOTH);
}

static void
#ifndef _NO_PROTO
PickSizes(XmTabStackWidget tab, int tab_width, int tab_height, XRectangle *box,
	  XRectangle *kid)
#else
PickSizes(tab, tab_width, tab_height, box, kid)
    XmTabStackWidget tab;
    int              tab_width, tab_height;
    XRectangle       *box, *kid;
#endif
{
    int              max, width, height, row_width, offset = 0,
	             num_rows, num_cols;
    XtWidgetGeometry ask, got;
    Boolean          stacked = False;

    /*
     * The first thing that we need to do is find out how much space we
     * will have for our children.  This is the space that is not
     * taken up by our XmTabBox Child.  We give first priority to the
     * XmTabBoxChild.
     */
    switch( XmTabStack_tab_side(tab) )
    {
    case XmTABS_ON_TOP:
    case XmTABS_ON_BOTTOM:
    default:
	/*
	 * For this case the amount of width is fixed so we want to know,
	 * given a fixed width what height are you interested in.
	 */
	ask.request_mode = CWWidth | CWHeight;
	ask.width = tab_width;
	ask.height = 0;
	XtQueryGeometry(XmTabStack_tab_box(tab), &ask, &got);

	/*
	 * Now that we have a response back from the kid we need to make
	 * sure that the height it wants is not larger than we can 
	 * accomidate.  If it is then we will scale it down.
	 */
	max = tab_height - 2 * BBPart(tab).margin_height -
	    tab->manager.shadow_thickness;

	if( (int)got.height > max ) got.height = max;

	width = tab_width - 2 * ((int)BBPart(tab).margin_width+
				 (int)tab->manager.shadow_thickness);
	height = max - (int)got.height;

	AssignMax(width, 1);
	AssignMax(height, 1);
	break;
    case XmTABS_ON_RIGHT:
    case XmTABS_ON_LEFT:
	/*
	 * For this case the amount of height is fixed so we want to know,
	 * given a fixed height what width are you interested in.
	 */
	ask.request_mode = CWWidth | CWHeight;
	ask.width = 0;
	ask.height = tab_height;
	XtQueryGeometry(XmTabStack_tab_box(tab), &ask, &got);

	/*
	 * Now that we have a response back from the kid we need to make
	 * sure that the width it wants is not larger than we can 
	 * accomidate.  If it is then we will scale it down.
	 */
	max = tab_width - 2 * BBPart(tab).margin_width -
	    tab->manager.shadow_thickness;

	if( (int)got.width > max ) got.height = max;

	height = tab_height - 2 *
	    ((int)BBPart(tab).margin_height +
	     (int)tab->manager.shadow_thickness);
	width = max - (int)got.width;

	AssignMax(width, 1);
	AssignMax(height, 1);
	break;
    }

    if( XmTabStack_stacked_effect(tab) &&
        (XmTabStack_tab_mode(tab) == XmTABS_STACKED ||
	 XmTabStack_tab_mode(tab) == XmTABS_STACKED_STATIC) )
    {
	Dimension doffset;

	stacked = True;
	XtVaGetValues(XmTabStack_tab_box(tab),
		      XmNtabOffset, &doffset,
		      NULL);
	offset = doffset;
    }

    /*
     * Now that we know the size of the XmTabBox we can now layout it and
     * our other children.  We will start out with the XmTabBox.
     */
    switch( XmTabStack_tab_side(tab) )
    {
    case XmTABS_ON_TOP:
    default:
	box->x = 0;
	box->y = 0;
	box->width = tab_width;
	box->height = got.height;
	kid->x =
	    BBPart(tab).margin_width + tab->manager.shadow_thickness;
	kid->y = got.height + BBPart(tab).margin_height;
	break;
    case XmTABS_ON_BOTTOM:
	box->x = 0;
	box->y = tab_height - (int)got.height;
	box->width = tab_width;
	box->height = got.height;
	kid->x =
	    BBPart(tab).margin_width + tab->manager.shadow_thickness;
	kid->y =
	    BBPart(tab).margin_height + tab->manager.shadow_thickness;
	break;
    case XmTABS_ON_LEFT:
	box->x = 0;
	box->y = 0;
	box->width = got.width;
	box->height = tab_height;
	kid->x = got.width + BBPart(tab).margin_height;
	kid->y =
	    BBPart(tab).margin_height + tab->manager.shadow_thickness;
	break;
    case XmTABS_ON_RIGHT:
	box->x = tab_width - (int)got.width;
	box->y = 0;
	box->width = got.width;
	box->height = tab_height;
	kid->x =
	    BBPart(tab).margin_width + tab->manager.shadow_thickness;
	kid->y =
	    BBPart(tab).margin_height + tab->manager.shadow_thickness;
	break;
    }

    /*
     * If we are doing a stacked layout we need to change the size of
     * our children to make room for some special effects. "Hey, Special
     * Effects ..."
     */
    if( stacked )
    {
	/*
	 * What we need to do is make the size of our child, the size of
	 * a row of tabs minus the shadows.
	 */
	switch( XmTabStack_tab_side(tab) )
	{
	case XmTABS_ON_TOP:
	case XmTABS_ON_BOTTOM:
	default:
	    _XmTabBoxGetNumRowsColumns(XmTabStack_tab_box(tab), tab_width,
				       &num_rows, &num_cols);

	    if( num_rows <= 1 || num_cols < 1 )
	    {
		width = tab_width - 2 * (BBPart(tab).margin_width +
					 tab->manager.shadow_thickness);
	    }
	    else
	    {
	        int tmp;
		if (LayoutIsRtoL(tab))
		    kid->x += offset * (num_rows-1);
		tmp = (((int)tab_width) - ((num_rows-1) * offset))/
		    num_cols;
		
		row_width = num_cols * tmp;
		width = row_width - 2 * (BBPart(tab).margin_width +
					 tab->manager.shadow_thickness);
	    }
	    break;
	case XmTABS_ON_LEFT:
	case XmTABS_ON_RIGHT:
	    _XmTabBoxGetNumRowsColumns(XmTabStack_tab_box(tab), tab_height,
				       &num_rows, &num_cols);

	    if( num_rows <= 1 || num_cols < 1 )
	    {
		height = tab_height -
		    2 * (BBPart(tab).margin_height +
			 tab->manager.shadow_thickness);
	    }
	    else
	    {
		int tmp = (((int)tab_height) - ((num_rows-1) * offset))/
		    num_cols;

		row_width = num_cols * tmp;
		height = row_width - 2 * (BBPart(tab).margin_height +
					  tab->manager.shadow_thickness);
	    }
	    break;
	}
    }

    kid->width = width;
    kid->height = height;
}

static void
#ifndef _NO_PROTO
Layout(XmTabStackWidget tab)
#else
Layout(tab)
    XmTabStackWidget tab;
#endif
{
    XRectangle box, child;
    WidgetList kid;
    int        i, neg_x;

    /*
     * Lets start by finding out what size everything should be.
     */
    PickSizes(tab, XtWidth(tab), XtHeight(tab), &box, &child);

    /*
     * Now that we know the sizes lets start by placing the tab box.
     */
    if( LocalTabBox(tab) )
    {
	_XmConfigureWidget(XmTabStack_tab_box(tab),
			   box.x, box.y, box.width, box.height, 0);
    }

    /*
     * ... And now place the kiddies.
     */
    for( i = 0, kid = tab->composite.children; i < tab->composite.num_children;
	 ++i, ++kid )
    {
	if( *kid == XmTabStack_tab_box(tab) || !XtIsManaged(*kid) ) continue;

	if( XmTabStack__active_child(tab) == *kid )
	{
	    _XmConfigureWidget(*kid, child.x, child.y,
			       child.width, child.height,
			       (*kid)->core.border_width);
	}
	else
	{
	    neg_x = -((int)child.width) - 2 *
		(int)((*kid)->core.border_width);

	    _XmConfigureWidget(*kid, (Position)neg_x, child.y, child.width,
			       child.height, (*kid)->core.border_width);
	}
    }
}

/*
 * Function:
 *	TabSelectedCallback(widget, client, cbdata)
 * Description:
 *	This callback is activated when the user selects a tab.  This
 *	callback brings the active child to the front and hides the
 *	old active child.
 * Input:
 *	widget : Widget    - the XmTabBox Widget
 *	client : XtPointer - unused
 *	cbdata : XtPointer - the callback data from the XmTabBox
 * Output:
 *	None.
 */
/* ARGSUSED */
static void
#ifndef _NO_PROTO
TabSelectedCallback(Widget widget, XtPointer client, XtPointer cbdata)
#else
TabSelectedCallback(widget, client, cbdata)
    Widget    widget;
    XtPointer client, cbdata;
#endif
{
    XmTabBoxCallbackStruct   *info = (XmTabBoxCallbackStruct*) cbdata;
    XmTabStackWidget         tab = (XmTabStackWidget) XtParent(widget);
    XmTabStackCallbackStruct data;
    WidgetList               kid = tab->composite.children;
    Widget                   old, active = NULL;
    Cardinal                 i, idx = 0;
    int                      neg_x, x, y, width, height;

    /*
     * It seems that a tab has been selected so we need to swap our
     * active widgets.  Lets start by grabbing a pointer to our soon
     * to be active widget.  To do this we have to walk our child list
     * and find our info->tab_index managed widget.
     */
    for( i = 0; i < tab->composite.num_children; ++i, ++kid )
    {
	if( !XtIsManaged(*kid) || IsTabBox(tab, *kid) ) continue;

	if( idx == info->tab_index )
	{
	    active = *kid;
	    break;
	}
	idx++;
    }

    /*
     * Lets do a quick check here to make sure we found something.  If not
     * something is probably seriously wrong so we will just bail.
     */
    if( active == NULL )
    {
	return;
    }
    old = XmTabStack__active_child(tab);

    /*
     * We know that our new active widget is going to be placed at the
     * same location and of the same dimensions as our currently active
     * child so lets more the new active child to those coords.
     */
    _XmConfigureWidget(active, old->core.x, old->core.y,
		       XtWidth(old), XtHeight(old), active->core.border_width);

    neg_x = -((int)XtWidth(old)) - 2 * (int)((*kid)->core.border_width);
    _XmMoveWidget(old, (Position)neg_x, old->core.y);

    /*
     * Now lets set the active child.
     */
    XmTabStack__active_child(tab) = active;
    x = active->core.x - BBPart(tab).margin_width;
    y = active->core.y - BBPart(tab).margin_height;
    width = XtWidth(active) + (2 * BBPart(tab).margin_width);
    height = XtHeight(active) + (2 * BBPart(tab).margin_height);

    /*
     * Now if the active child has a background specified, or if the old
     * active child had one, then we need to redraw our background.
     */
    if( XiBackgroundSpecified(old) ||
        XiBackgroundSpecified(active) ||
        XiSelectSpecified(tab) )
    {
	if( XiSelectSpecified(tab) )
	{
	    SetSelectGC(tab, XmTabStack__gc(tab));
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), XmTabStack__gc(tab),
			   x, y, width, height);
	}
	else if( XiBackgroundSpecified(active) )
	{
	    SetChildGC(active, XmTabStack__gc(tab));
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), XmTabStack__gc(tab),
			   x, y, width, height);
	}
	else
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab),
			   tab->manager.background_GC,
			   x, y, width, height);
	}
    }

    /*
     * Now, if we are stacked, lets check to see if are old child
     * and our newly selected child are on the same row, and if
     * not, and we are not a perfect square (all rows and columns filled)
     * then we need to redraw our stacked shadows.
     */
    if( ((XmTabStack_tab_mode(tab) == XmTABS_STACKED ||
	  XmTabStack_tab_mode(tab) == XmTABS_STACKED_STATIC) &&
          XmTabStack_stacked_effect(tab)) &&
        (info->old_index >= 0 && info->tab_index >= 0 &&
	 XmTabBoxGetTabRow(XmTabStack_tab_box(tab), info->old_index) != 
	 XmTabBoxGetTabRow(XmTabStack_tab_box(tab), info->tab_index)) )
    {
	DrawStackedShadows(tab, tab->manager.top_shadow_GC,
			   tab->manager.bottom_shadow_GC,
			   0, 0, XtWidth(active) +
			   (2 * BBPart(tab).margin_width),
			   XtHeight(active) +
			   (2 * BBPart(tab).margin_height));
    }

    /*
     * And finally lets call the select callback for the XmTabStack
     * so that the user knows something has changed.
     */
    if (XmTabStack_do_notify(tab))
    {
	data.reason = XmCR_TAB_SELECTED;
	data.event = info->event;
	data.selected_child = active;

	XtCallCallbackList((Widget)tab, XmTabStack_tab_select_callback(tab), (XtPointer)&data);
    }
}

/*
 * XmRCallProc routine for checking font_list before setting it to NULL
 * If "check_set_render_table" is True, then function has 
 * been called twice on same widget, thus resource needs to be set NULL, 
 * otherwise leave it alone.
 */

/*ARGSUSED*/
static void 
CheckSetRenderTable(Widget wid,
		    int offs,
		    XrmValue *value)
{
  XmTabStackWidget lw = (XmTabStackWidget)wid;

  /* Check if been here before */
  if (lw->tab_stack.check_set_render_table)
      value->addr = NULL;
  else {
      lw->tab_stack.check_set_render_table = True;
      value->addr = (char*)&(lw->tab_stack.font_list);
  }

}

#ifdef UNUSED_FUNCTION
/* ARGSUSED */
static Widget
#ifndef _NO_PROTO
IndexToTab(XmTabStackWidget tab, int idx)
#else
IndexToTab(tab, idx)
    XmTabStackWidget tab;
    int              idx;
#endif
{
    WidgetList kid = tab->composite.children;
    int i;

    if( tab == NULL || idx < 0 ) return( (Widget)NULL );

    for( i = 0; kid != NULL; kid++ )
    {
	if( !IsValidChild(tab, *kid) ) continue;
	if( i == idx )
	{
	    return( *kid );
	}
	++i;
    }
    return( (Widget)NULL );
}
#endif

static int
#ifndef _NO_PROTO
TabToIndex(XmTabStackWidget tab, Widget child)
#else
TabToIndex(tab, child)
    XmTabStackWidget tab;
    Widget           child;
#endif
{
    WidgetList kid;
    int i;

    if( tab == NULL || child == NULL ) return( -1 );
    
    kid = tab->composite.children;

    for( i = 0; kid != NULL; kid++ )
    {
	if( !IsValidChild(tab, *kid) ) continue;
	if( *kid == child ) return( i );
	++i;
    }
    return( -1 );
}

Widget
#ifndef _NO_PROTO
XmCreateTabStack(Widget parent, String name, ArgList arg_list,
		 Cardinal arg_cnt)
#else
XmCreateTabStack(parent, name, arg_list, arg_cnt)
    Widget   parent;
    String   name;
    ArgList  arg_list;
    Cardinal arg_cnt;
#endif
{
    return( XtCreateWidget(name, xmTabStackWidgetClass, parent, arg_list,
			   arg_cnt) );
}

Widget 
XmVaCreateTabStack(
        Widget parent,
        char *name,
        ...)
{
    register Widget w;
    va_list var;
    int count;
    
    Va_start(var,name);
    count = XmeCountVaListSimple(var);
    va_end(var);

    
    Va_start(var, name);
    w = XmeVLCreateWidget(name, 
                         xmTabStackWidgetClass,
                         parent, False, 
                         var, count);
    va_end(var);   
    return w;
}

Widget
XmVaCreateManagedTabStack(
        Widget parent,
        char *name,
        ...)
{
    Widget w = NULL;
    va_list var;
    int count;
    
    Va_start(var, name);
    count = XmeCountVaListSimple(var);
    va_end(var);
    
    Va_start(var, name);
    w = XmeVLCreateWidget(name, 
                         xmTabStackWidgetClass,
                         parent, True, 
                         var, count);
    va_end(var);   
    return w;
}

void
#ifndef _NO_PROTO
XmTabStackSelectTab(Widget widget, Boolean notify)
#else
XmTabStackSelectTab(widget, notify)
    Widget widget;
    Boolean notify;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) XtParent(widget);

    _XmWidgetToAppContext(widget);
    _XmAppLock(app);

    if (!XmIsTabStack((Widget)tab)) 
      {
	_XmAppUnlock(app); 
	return;
      }

    if (!XtIsRealized((Widget)tab))
    {
	/* wherehouse the selected widget id to use later in realize proc */
	XmTabStack__selected_tab(tab) = widget;
	XmTabStack__selected_notify(tab) = notify;
    }
    else
    {
	/* 
	 * _XmTabBoxSelectTab will call our internal callback routine,
	 * TabSelectedCallback() which in turn calls the XmNtabSelectedCallback
	 * callbacks if the "do_notify" flag is set...  The do_notify flag is
	 * normally TRUE, but can be switched off through this interface.
	 */
	XmTabStack_do_notify(tab) = notify;

	_XmTabBoxSelectTab(XmTabStack_tab_box(tab), TabToIndex(tab, widget));

	XmTabStack_do_notify(tab) = True;
    }

    _XmAppUnlock(app);  
}

static void
#ifndef _NO_PROTO
DrawStackedShadows(XmTabStackWidget tab, GC top_GC, GC bottom_GC, int x, int y,
		   int base_width, int base_height)
#else
DrawStackedShadows(tab, top_GC, bottom_GC, x, y, base_width, base_height)
    XmTabStackWidget tab;
    GC               top_GC, bottom_GC;
    int              x, y, base_width, base_height;
#endif
{
    Widget     parent = XtParent(tab);
    int        i, num_rows, num_cols, offset, shadow, y2, x2, idx;
    Dimension  doffset;
    GC         gc;
    XRectangle rts[2];
    Pixel      pixel = XmUNSPECIFIED_PIXEL;
    Pixmap     pixmap = None;

    shadow = tab->manager.shadow_thickness;
    num_rows = XmTabBoxGetNumRows(XmTabStack_tab_box(tab));
    num_cols = XmTabBoxGetNumColumns(XmTabStack_tab_box(tab));
    XtVaGetValues(XmTabStack_tab_box(tab),
		  XmNtabOffset, &doffset,
		  NULL);
    offset = doffset;

    if( !XmIsManager(parent) )
    {
	XtVaGetValues(parent,
		      XmNbackground, &pixel,
		      XmNbackgroundPixmap, &pixmap,
		      NULL);
    }

    switch( XmTabStack_tab_side(tab) )
    {
    case XmTABS_ON_TOP:
    default:
	if (LayoutIsRtoLM(tab)) {
	    x = 0;
	    y = XmTabStack_tab_box(tab)->core.height;

	    XFillRectangle(XtDisplay(tab), XtWindow(tab), bottom_GC,
		       x + shadow + offset * (num_rows - 1), y + base_height,
		       base_width + shadow, shadow);

	    x += offset * (num_rows - 1);
	    x2 = x + offset;
	    y2 = y + base_height;

	    for( i = 0; i < num_rows; ++i )
	    {
	    	if (i == 0)
		{
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.top_shadow_GC,
			       x, y, shadow, y2 - y);
		    XmDrawBevel(XtDisplay(tab), XtWindow(tab), top_GC, bottom_GC,
				   x, y2, shadow, XmBEVEL_BOTH);
		    y2 -= offset;
		    x -= offset;
		    x2 -= offset;
		}
		else
		{
		    if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
					  (num_cols-1))) >= 0 )
		    {
			Widget child = XmTabStackIndexToWidget((Widget)tab, idx);

			if( child == NULL ||
				!XiBackgroundSpecified(child) )
			{
			    gc = tab->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    SetChildGC(child, gc);
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);

			if( XmIsManager(parent) )
			{
			    gc = ((XmManagerWidget)parent)->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    if( ValidPixmap(pixmap) )
			    {
				SetTiledGC(XtDisplay(tab), gc, pixmap);
			    }
		    	    else
			    {
				SetSolidGC(XtDisplay(tab), gc, pixel);
			    }
		        }
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y2, x2 - x, (int)XtHeight(tab) - y2);

			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.top_shadow_GC,
				   x, y, shadow, y2 - y);
			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x, y2 - shadow, x2 - x, shadow);
			XmDrawBevel(XtDisplay(tab), XtWindow(tab), top_GC, bottom_GC,
				   x, y2 - shadow, shadow, XmBEVEL_BOTH);
			x2 -= offset;
		    }
		    x -= offset;
		    y2 -= offset;
		}
	    } /* for */
	    if( XmIsManager(parent) )
	    {
		gc = ((XmManagerWidget)parent)->manager.background_GC;
	    }
	    else
	    {
		gc = XmTabStack__gc(tab);
		if( ValidPixmap(pixmap) )
		{
		    SetTiledGC(XtDisplay(tab), gc, pixmap);
		}
		else
		{
		    SetSolidGC(XtDisplay(tab), gc, pixel);
		}
	    }
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			offset * (num_rows-1) + base_width + 2*shadow, 0,
			XtWidth(tab) - offset * (num_rows-1) + base_width + 2*shadow, XtHeight(tab));
	    if( _XiGetTabIndex(XmTabStack_tab_box(tab), num_rows-1,
		    num_cols-1) < 0 )
	    {
	        XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			0, 0, offset, XtHeight(tab));
	    }
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), bottom_GC,
		       offset * (num_rows - 1) + base_width + shadow, y,
		       shadow, base_height + shadow);
	} else {
	    x = 0;
	    y = XmTabStack_tab_box(tab)->core.height;

	    XFillRectangle(XtDisplay(tab), XtWindow(tab), top_GC,
		       x, y, shadow, base_height + shadow);
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), bottom_GC,
		       x + shadow, y + base_height, base_width + shadow,
		       shadow);
	    XmDrawBevel(XtDisplay(tab), XtWindow(tab), top_GC, bottom_GC,
		    x, y + base_height, shadow, XmBEVEL_BOTTOM);

	    x += base_width + shadow;
	    x2 = x + offset;
	    y2 = y + base_height;
	    for( i = 0; i < num_rows; ++i )
	    {
		if( i == 0 )
		{
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.bottom_shadow_GC,
			       x, y, shadow, y2 - y);
		    x += shadow;
		    x2 = x + offset;
		}
		else
		{
		    if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
					  (num_cols-1))) >= 0 )
		    {
			Widget child = XmTabStackIndexToWidget((Widget)tab, idx);

			if( child == NULL || !XiBackgroundSpecified(child) )
			{
			    gc = tab->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    SetChildGC(child, gc);
			}
		        XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);

			if( XmIsManager(parent) )
			{
			    gc = ((XmManagerWidget)parent)->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    if( ValidPixmap(pixmap) )
			    {
				SetTiledGC(XtDisplay(tab), gc, pixmap);
			    }
			    else
			    {
				SetSolidGC(XtDisplay(tab), gc, pixel);
			    }
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y2, x2 - x, (int)XtHeight(tab) - y2);

			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x2 - shadow, y, shadow, y2 - y);
			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x, y2 - shadow, x2 - x, shadow);
			x = x2;
		    }
		    x2 += offset;
		}
	        y2 -= offset;
	    }
	    

	    if( XmIsManager(parent) )
	    {
		gc = ((XmManagerWidget)parent)->manager.background_GC;
	    }
	    else
	    {
		gc = XmTabStack__gc(tab);
		if( ValidPixmap(pixmap) )
		{
		    SetTiledGC(XtDisplay(tab), gc, pixmap);
		}
		else
		{
		    SetSolidGC(XtDisplay(tab), gc, pixel);
		}
	    }
	    if( x < (int)XtWidth(tab) )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   x, 0, (int)XtWidth(tab) - x, XtHeight(tab));
	    }
	}
	break;
    case XmTABS_ON_BOTTOM:
	if( LayoutIsRtoL(tab) )
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), bottom_GC,
		    offset * (num_rows - 1) + base_width + shadow, 0, shadow, XtHeight(tab));
	
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), top_GC,
		    offset * (num_rows - 1), 0,
		    base_width + shadow, shadow);

	    x = offset * (num_rows - 1);
	    y = 0;
	    x2 = x + offset;
	    y2 = y + offset;

	    for( i = 0; i < num_rows; ++i )
	    {
	    	if (i == 0)
		{
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.top_shadow_GC,
			       x, y + shadow, shadow, base_height);
		    x -= offset;
		    x2 -= offset;
		}
		else
		{
		    if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
			(num_cols-1))) >= 0 )
		    {
			Widget child = XmTabStackIndexToWidget((Widget)tab, idx);

			if( child == NULL || !XiBackgroundSpecified(child) )
			{
			    gc = tab->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    SetChildGC(child, gc);
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y2 + shadow, x2 - x, base_height - y2);

			if( XmIsManager(parent) )
			{
			    gc = ((XmManagerWidget)parent)->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    if( ValidPixmap(pixmap) )
			    {
				SetTiledGC(XtDisplay(tab), gc, pixmap);
			    }
			    else
			    {
				SetSolidGC(XtDisplay(tab), gc, pixel);
			    }
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);
			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.top_shadow_GC,
				   x, y2 + shadow, shadow, base_height - y2);
			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.top_shadow_GC,
				   x, y2, x2 - x, shadow);
			
			x2 -= offset;
		    }
		    x -= offset;
		    y2 += offset;
		}
	    }
	    
	    if( XmIsManager(parent) )
	    {
		gc = ((XmManagerWidget)parent)->manager.background_GC;
	    }
	    else
	    {
		gc = XmTabStack__gc(tab);
		if( ValidPixmap(pixmap) )
		{
		    SetTiledGC(XtDisplay(tab), gc, pixmap);
		}
		else
		{
		    SetSolidGC(XtDisplay(tab), gc, pixel);
		}
	    }
	    i = offset * (num_rows-1) + base_width + shadow*2;
	    if( i < (int)XtWidth(tab) )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   i, 0, (int)XtWidth(tab) - i, XtHeight(tab));
	    }
	    if( _XiGetTabIndex(XmTabStack_tab_box(tab), num_rows-1,
		    num_cols-1) < 0 )
	    {
	        XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			0, 0, offset, XtHeight(tab));
	    }
	}
	else
	{
	    rts[0].x = 0;
	    rts[0].y = 0;
	    rts[0].width = shadow;
	    rts[0].height = XtHeight(tab);

	    rts[1].x = shadow;
	    rts[1].y = 0;
	    rts[1].width = base_width + shadow;
	    rts[1].height = shadow;
	    XFillRectangles(XtDisplay(tab), XtWindow(tab), top_GC, rts, 2);
	    x += base_width + shadow;

	    XmDrawBevel(XtDisplay(tab), XtWindow(tab),
		    tab->manager.top_shadow_GC,
		    tab->manager.bottom_shadow_GC,
		    x, 0, shadow, XmBEVEL_BOTTOM);

	    x2 = x + offset;
	    y += shadow;
	    y2 = y + base_height;
	    for( i = 0; i < num_rows; ++i )
	    {
		if( i == 0 )
		{
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.bottom_shadow_GC,
			       x, y, shadow, y2 - y);
		
		    x += shadow;
		    x2 = x + offset;
	        }
		else
		{
		    if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
					  (num_cols-1))) >= 0 )
		    {
			Widget child =
				XmTabStackIndexToWidget((Widget)tab, idx);

			if( child == NULL || !XiBackgroundSpecified(child) )
			{
			    gc = tab->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    SetChildGC(child, gc);
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);

	
			if( XmIsManager(parent) )
			{
			    gc = ((XmManagerWidget)parent)->manager.background_GC;
			}
			else
			{
			    gc = XmTabStack__gc(tab);
			    if( ValidPixmap(pixmap) )
			    {
				SetTiledGC(XtDisplay(tab), gc, pixmap);
			    }
			    else
			    {
				SetSolidGC(XtDisplay(tab), gc, pixel);
			    }
			}
			XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, 0, x2 - x, y);

			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x2 - shadow, y, shadow, y2 - y);
			XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.top_shadow_GC,
				   x, y, x2 - x, shadow);
			XmDrawBevel(XtDisplay(tab), XtWindow(tab),
				tab->manager.top_shadow_GC,
				tab->manager.bottom_shadow_GC,
				x2 - shadow, y, shadow, XmBEVEL_BOTTOM);
			x = x2;
		    }
		    x2 += offset;
		}
		y += offset;
	    }
	    if( XmIsManager(parent) )
	    {
		gc = ((XmManagerWidget)parent)->manager.background_GC;
	    }
	    else
	    {
		gc = XmTabStack__gc(tab);
		if( ValidPixmap(pixmap) )
		{
		    SetTiledGC(XtDisplay(tab), gc, pixmap);
		}
		else
		{
		    SetSolidGC(XtDisplay(tab), gc, pixel);
		}
	    }
	    if( x < (int)XtWidth(tab) )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   x, 0, (int)XtWidth(tab) - x, XtHeight(tab));
	    }
	}
	break;
    case XmTABS_ON_RIGHT:
	rts[0].x = 0;
	rts[0].y = 0;
	rts[0].width = XtWidth(tab);
	rts[0].height = shadow;

	rts[1].x = 0;
	rts[1].y = shadow;
	rts[1].width = shadow;
	rts[1].height = base_width + shadow;
	XFillRectangles(XtDisplay(tab), XtWindow(tab), top_GC, rts, 2);
	y += base_height + shadow;
	
	XmDrawBevel(XtDisplay(tab), XtWindow(tab),
		    tab->manager.top_shadow_GC,
		    tab->manager.bottom_shadow_GC,
		    0, y, shadow, XmBEVEL_BOTTOM);
	y2 = y + offset;
	x += shadow;
	x2 = y + base_width;
	for( i = 0; i < num_rows; ++i )
	{
	    if( i == 0 )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.bottom_shadow_GC,
			       x, y, x2 - x, shadow);
		y += shadow;
		y2 = y + offset;
	    }
	    else
	    {
		if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
					  (num_cols-1))) >= 0 )
		{
		    Widget child = XmTabStackIndexToWidget((Widget)tab, idx);
		    
		    if( child == NULL || !XiBackgroundSpecified(child) )
		    {
			gc = tab->manager.background_GC;
		    }
		    else
		    {
			gc = XmTabStack__gc(tab);
			SetChildGC(child, gc);
		    }
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);
		    
		    if( XmIsManager(parent) )
		    {
			gc = ((XmManagerWidget)parent)->manager.background_GC;
		    }
		    else
		    {
			gc = XmTabStack__gc(tab);
			if( ValidPixmap(pixmap) )
			{
			    SetTiledGC(XtDisplay(tab), gc, pixmap);
			}
			else
			{
			    SetSolidGC(XtDisplay(tab), gc, pixel);
			}
		    }
		    
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   0, y, x, y2 - y);
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), 
				   tab->manager.bottom_shadow_GC,
				   x, y2 - shadow, x2 - x, shadow);
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), 
				   tab->manager.top_shadow_GC,
				   x, y, shadow, y2 - y);
		    XmDrawBevel(XtDisplay(tab), XtWindow(tab),
				tab->manager.top_shadow_GC,
				tab->manager.bottom_shadow_GC,
				x, y2 - shadow, shadow, XmBEVEL_BOTTOM);
		    y = y2;
		}
		y2 += offset;
	    }
	    x += offset;
	}
	if( XmIsManager(parent) )
	{
	    gc = ((XmManagerWidget)parent)->manager.background_GC;
	}
	else
	{
	    gc = XmTabStack__gc(tab);
	    if( ValidPixmap(pixmap) )
	    {
		SetTiledGC(XtDisplay(tab), gc, pixmap);
	    }
	    else
	    {
		SetSolidGC(XtDisplay(tab), gc, pixel);
	    }
	}
	if( x < (int)XtWidth(tab) )
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   0, y, XtWidth(tab), (int)XtHeight(tab) - y);
	}
	break;
    case XmTABS_ON_LEFT:
	y = 0;
	x = XtWidth(XmTabStack_tab_box(tab));

	XFillRectangle(XtDisplay(tab), XtWindow(tab), top_GC,
		       x, y, base_width + shadow, shadow);
	XFillRectangle(XtDisplay(tab), XtWindow(tab), bottom_GC,
		       x + base_width, y + shadow, shadow,
		       base_height + shadow);
	XmDrawBevel(XtDisplay(tab), XtWindow(tab), top_GC, bottom_GC,
		    x + base_width, y, shadow, XmBEVEL_BOTTOM);

	y += base_height + shadow;
	y2 = y + offset;
	x2 = x + base_width;
	for( i = 0; i < num_rows; ++i )
	{
	    if( i == 0 )
	    {
		XFillRectangle(XtDisplay(tab), XtWindow(tab),
			       tab->manager.bottom_shadow_GC,
			       x, y, x2 - x, shadow);
		y += shadow;
		y2 = y + offset;
	    }
	    else
	    {
		if( (idx = _XiGetTabIndex(XmTabStack_tab_box(tab), i,
					  (num_cols-1))) >= 0 )
		{
		    Widget child = XmTabStackIndexToWidget((Widget)tab, idx);

		    if( child == NULL || !XiBackgroundSpecified(child) )
		    {
			gc = tab->manager.background_GC;
		    }
		    else
		    {
			gc = XmTabStack__gc(tab);
			SetChildGC(child, gc);
		    }
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x, y, x2 - x, y2 - y);

	
		    if( XmIsManager(parent) )
		    {
			gc = ((XmManagerWidget)parent)->manager.background_GC;
		    }
		    else
		    {
			gc = XmTabStack__gc(tab);
			if( ValidPixmap(pixmap) )
			{
			    SetTiledGC(XtDisplay(tab), gc, pixmap);
			}
			else
			{
			    SetSolidGC(XtDisplay(tab), gc, pixel);
			}
		    }
		    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
				   x2, y, (int)XtWidth(tab) - x2, y2 - y);
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x, y2 - shadow, x2 - x, shadow);
		    XFillRectangle(XtDisplay(tab), XtWindow(tab),
				   tab->manager.bottom_shadow_GC,
				   x2 - shadow, y, shadow, y2 - y);
		    y = y2;
		}
		y2 += offset;
	    }
	    x2 -= offset;
	}
	if( XmIsManager(parent) )
	{
	    gc = ((XmManagerWidget)parent)->manager.background_GC;
	}
	else
	{
	    gc = XmTabStack__gc(tab);
	    if( ValidPixmap(pixmap) )
	    {
		SetTiledGC(XtDisplay(tab), gc, pixmap);
	    }
	    else
	    {
		SetSolidGC(XtDisplay(tab), gc, pixel);
	    }
	}
	if( x < (int)XtWidth(tab) )
	{
	    XFillRectangle(XtDisplay(tab), XtWindow(tab), gc,
			   0, y, XtWidth(tab), (int)XtHeight(tab) - y);
	}
	break;
    }
}


/* note: intentionally undocumented. It may be here for historical reasons
** so keep it around; but the function isn't useful. It's used internally
** and was probably intended not to be publicized.
*/
Widget
#ifndef _NO_PROTO
XmTabStackIndexToWidget(Widget widget, int idx)
#else
XmTabStackIndexToWidget(widget, idx)
    Widget widget;
    int    idx;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) widget;
    Cardinal         i, cnt;
    WidgetList       kid;

    _XmWidgetToAppContext(widget);
    _XmAppLock(app);

    if( !XmIsTabStack(widget) || idx < 0 )
    {
	_XmAppUnlock(app); 
	return( NULL );
    }

    for( i = 0, cnt = 0, kid = tab->composite.children;
	 i < tab->composite.num_children; ++i, ++kid )
    {
	if( IsTabBox(tab, *kid) || !XtIsManaged(*kid) ) continue;
	if( cnt++ == idx ) 
	  {
	    _XmAppUnlock(app); 
	    return( *kid );
	  }
    }
    _XmAppUnlock(app); 
    return( NULL );
}

Widget
#ifndef _NO_PROTO
XmTabStackGetSelectedTab(Widget widget)
#else
XmTabStackGetSelectedTab(widget)
    Widget widget;
#endif
{
    XmTabStackWidget tab = (XmTabStackWidget) widget;

    _XmWidgetToAppContext(widget);
    _XmAppLock(app);

    if (!XmIsTabStack(widget))
    {
	_XmAppUnlock(app); 
	return(NULL);
    }

    if (!XtIsRealized((Widget)tab) && XmTabStack__selected_tab(tab))
      {
	_XmAppUnlock(app); 
	return (XmTabStack__selected_tab(tab));
      }

    _XmAppUnlock(app);    
    return (XmTabStack__active_child(tab));
}

#ifdef TEAR_OFF_TABS

static void
#ifndef _NO_PROTO
TearOffCallback(Widget widget, XtPointer client, XtPointer cbdata)
#else
TearOffCallback(widget, client, cbdata)
    Widget    widget;
    XtPointer client, cbdata;
#endif
{
    XtPointer data;

    XtVaGetValues(widget, XmNuserData, &data, NULL);
    if( data == NULL ) return;
}

static void
#ifndef _NO_PROTO
MenuSelectCallback(Widget widget, XtPointer client, XtPointer cbdata)
#else
MenuSelectCallback(widget, client, cbdata)
    Widget    widget;
    XtPointer client, cbdata;
#endif
{
    XtPointer data;

    XtVaGetValues(widget,
		  XmNuserData, &data,
		  NULL);

    XmTabStackSelectTab((Widget)data);
}

static void
#ifndef _NO_PROTO
BuildMenu(XmTabStackWidget tab)
#else
BuildMenu(tab)
    XmTabStackWidget tab;
#endif
{
    WidgetList kids, buttons;
    Cardinal   kidCnt, validKids, i, j, buttonCnt, validButtons;
    Widget     menu = XmTabStack__menu(tab), tmp;
    Boolean    done;

    /*
     * First thing we need to do is get the list of child for the
     * menu.
     */
    XtVaGetValues(tab,
		  XmNchildren, &kids,
		  XmNnumChildren, &kidCnt,
		  NULL);

    for( i = 0, validKids = 0; i < kidCnt; ++i )
    {
	if( IsValidChild(tab, kids[i]) ) validKids++;
    }

    XtVaGetValues(menu,
		  XmNchildren, &buttons,
		  XmNnumChildren, &buttonCnt,
		  NULL);
    
    for( i = 0, validButtons = 0; i < buttonCnt; ++i )
    {
	if( XmIsPushButton(buttons[i]) ) validButtons++;
    }

    for( i = validButtons; i <= validKids; ++i )
    {
	tmp = 
	    XtVaCreateWidget("menuItem", xmPushButtonWidgetClass, menu, NULL);
	XtAddCallback(tmp, XmNactivateCallback, MenuSelectCallback, NULL);
    }

    XtVaGetValues(menu,
		  XmNchildren, &buttons,
		  XmNnumChildren, &buttonCnt,
		  NULL);

    for( j = 0; j < buttonCnt; ++j )
    {
	if( !XmIsSeparator(buttons[j]) &&
	    buttons[j] != XmTabStack__tear_off_button(tab) ) break;
    }
    for( i = 0, done = False; i < kidCnt;  ++i)
    {
	if( !IsValidChild(tab, kids[i]) ) continue;

	XtVaSetValues(buttons[j],
		      XmNlabelString, XmTabStackC_tab_label_string(kids[i]),
		      XmNuserData, (XtPointer) kids[i],
		      NULL);

	XtManageChild(buttons[j]);
	for( j++; j < buttonCnt; ++j )
	{
	    if( !XmIsSeparator(buttons[j]) &&
	        buttons[j] != XmTabStack__tear_off_button(tab) ) break;
	}
    }

    for( ; j < buttonCnt; ++j )
    {
	if( XmIsSeparator(buttons[j]) ) continue;

	XtUnmanageChild(buttons[j]);
    }
}

static void
#ifndef _NO_PROTO
TabTransferProc(Widget widget, XtPointer closure, Atom *selType,
		Atom *type, XtPointer value, unsigned long *length,
		int format)
#else
TabTransferProc(widget, closure, selType, type, value, length, format)
    Widget        widget;
    XtPointer     closure;
    Atom          *selType, *type;
    XtPointer     value;
    unsigned long *length;
    int           format;
#endif
{
    Widget   child, tmp, tab = (Widget)closure;
    Colormap c_cmap, p_cmap;
    Cardinal c_depth, p_depth;

    if( *length != sizeof(Widget) ||
        *type != WidgetAtom(widget) )
    {
	XtVaSetValues(widget,
		      XmNtransferStatus, XmTRANSFER_FAILURE,
		      NULL);
	return;
    }

    child = *((Widget*)value);

    /*
     * Lets just do a quick validity check here. Also if the child is
     * already a child of the tab stack then lets fail.
     */
    if( child->core.self != child || tab->core.self != tab ||
        XtParent(child) == tab )
    {
	XtVaSetValues(widget,
		      XmNtransferStatus, XmTRANSFER_FAILURE,
		      NULL);
	return;
    }

    /*
     * Now lets check some specific values to make sure that the
     * transfer will not cause an X Error.
     */
    XtVaGetValues(child,
		  XmNcolormap, &c_cmap,
		  XmNdepth, &c_depth,
		  NULL);

    XtVaGetValues(tab,
		  XmNcolormap, &p_cmap,
		  XmNdepth, &p_depth,
		  NULL);

    if( c_cmap != p_cmap || c_depth != p_depth )
    {
	XtVaSetValues(widget,
		      XmNtransferStatus, XmTRANSFER_FAILURE,
		      NULL);
	return;
    }

    /*
     * The final safty check is to see if we are trying to move a parent
     * into a child.
     */
    for( tmp = tab; tmp != NULL && tmp != child; tmp = XtParent(tmp) );
    if( tmp == child )
    {
	XtVaSetValues(widget,
		      XmNtransferStatus, XmTRANSFER_FAILURE,
		      NULL);
	return;
    }

    /*
     * If we got here that means that we really want to move this
     * panel so lets do it.
     */
    XiMoveTabPanel(child, tab);
    XtVaSetValues(widget,
		  XmNtransferStatus, XmTRANSFER_SUCCESS,
		  NULL);
}

static void
#ifndef _NO_PROTO
HandleTabDrop(Widget widget, XtPointer client, XtPointer cbdata)
#else
HandleTabDrop(widget, client, cbdata)
    Widget    widget;
    XtPointer client, cbdata;
#endif
{
    XmDropProcCallback     drop = (XmDropProcCallback) cbdata;
    XmDropTransferEntryRec transferEntries[2];
    XmDropTransferEntry    transferList;
    Arg                    args[10];
    int                    n = 0;

    if( drop->dropAction != XmDROP ||
        drop->operation != XmDROP_MOVE )
    {
	XtSetArg(args[n], XmNtransferStatus, XmTRANSFER_FAILURE); ++n;
    }
    else
    {
	transferEntries[0].target = TabAtom(widget);
	transferEntries[0].client_data = (XtPointer) widget;
	transferList = transferEntries;
	XtSetArg(args[n], XmNdropTransfers, transferList); ++n;
	XtSetArg(args[n], XmNnumDropTransfers, 1); ++n;
	XtSetArg(args[n], XmNtransferProc, TabTransferProc); ++n;
	XtSetArg(args[n], XmNtransferStatus, XmTRANSFER_SUCCESS); ++n;
    }
    XmDropTransferStart(drop->dragContext, args, n);
}

static Boolean
#ifndef _NO_PROTO
TabConvertProc(Widget widget, Atom *selection, Atom *target, Atom *typeRtn,
	       XtPointer *valueRtn, unsigned long *lengthRtn, int *formatRtn,
	       unsigned long *max_lengthRtn, XtPointer client_data,
	       XtRequestId request_id)
#else
TabConvertProc(widget, selection, target, typeRtn, valueRtn, lengthRtn, 
	       formatRtn, max_lengthRtn, client_data, request_id)
    Widget        widget;
    Atom          *selection, *target, *typeRtn;
    XtPointer     *valueRtn;
    unsigned long *lengthRtn;
    int           *formatRtn;
    unsigned long *max_lengthRtn;
    XtPointer     client_data;
    XtRequestId   request_id;
#endif
{
    Widget    child, *passed_child;
    XtPointer child_data;

    XtVaGetValues(widget,
		  XmNclientData, &child_data,
		  NULL);
    child = (Widget) child_data;

    if( *target != TabAtom(widget) ) return( False );

    passed_child = XtNew(Widget);
    *passed_child = child;

    *typeRtn = WidgetAtom(widget);
    *valueRtn = (XtPointer) passed_child;
    *lengthRtn = sizeof(Widget);
    *formatRtn = 8;

    return( True );
}

static Atom
#ifndef _NO_PROTO
TabAtom(Widget widget)
#else
TabAtom(widget)
    Widget widget;
#endif
{
    char pid_buf[64];

    sprintf(pid_buf, "ICS_TAB_PID_%d\n", (int)getpid());
    return( XmInternAtom(XtDisplay(widget), pid_buf, False) );
}

Widget
#ifndef _NO_PROTO
XmTabStackXYToWidget(Widget widget, int x, int y)
#else
XmTabStackXYToWidget(widget, x, y)
    Widget widget;
    int    x, y;
#endif
{
    Widget tab_stack, tab_box;

    _XmWidgetToAppContext(widget);
    _XmAppLock(app);

    if( XmIsTabStack(widget) )
    {
	tab_stack = widget;
	tab_box = XmTabStack_tab_box((XmTabStackWidget)widget);

    }
    else if( XmIsTabBox(widget) )
    {
	tab_stack = XtParent(widget);
	tab_box = widget;
    }
    else if( XtParent(widget) != NULL && XmIsTabBox(XtParent(widget)) )
    {
	
	tab_box = XtParent(widget);
	tab_stack = XtParent(tab_box);
    }
    else
    {
      _XmAppUnlock(app); 
      return( NULL );
    }

    _XmAppUnlock(app); 
    return( XmTabStackIndexToWidget(tab_stack,
				    XmTabBoxXYToIndex(tab_box, x, y)) );
}

#define XiDeleteChild(c) \
{ \
     _XmProcessLock(); \
     (*((CompositeWidgetClass)((c)->core.parent->core.widget_class))-> \
      composite_class.delete_child)(c); \
    _XmProcessUnlock(); \
}


#define XiInsertChild(p,c) \
{ \
     (c)->core.parent = (p); \
     _XmProcessLock(); \
     (*((CompositeWidgetClass)(p)->core.widget_class)-> \
      composite_class.insert_child)(c); \
     _XmProcessUnlock(); \
}


static void
#ifndef _NO_PROTO
XiMoveTabPanel(Widget panel, Widget to)
#else
XiMoveTabPanel(panel, to)
    Widget panel, to;
#endif
{
    Widget from = XtParent(panel);
    int    width, height;

    /*
     * The object here is to move the panel from "from" to "to", so lets
     * start by removing the panel from "from".  This is done by first
     * unmanaging the panel and then calling "from"s delete method.
     */
    XmDropSiteStartUpdate(from);
    XmDropSiteStartUpdate(to);
    width = XmTabStackC_width(panel);
    height = XmTabStackC_height(panel);
    XtUnmanageChild(panel);
    XtVaSetValues(panel,
		  XmNwidth, width,
		  XmNheight, height,
		  NULL);
    XiDeleteChild(panel);
    XtUnrealizeWidget(panel);

    /*
     * And now lets add that child to "to" by calling its insert method
     * and then managing the puppy.
     */
    XiInsertChild(to, panel);
    XtManageChild(panel);
    XmDropSiteEndUpdate(from);
    XmDropSiteEndUpdate(to);
}

static void
#ifndef _NO_PROTO
DragCallback(Widget widget, XtPointer client, XtPointer cbdata)
#else
DragCallback(widget, client, cbdata)
    Widget    widget;
    XtPointer client, cbdata;
#endif
{
    XmAnyCallbackStruct            *info = (XmAnyCallbackStruct*)cbdata;
    XmTabStackWidget               tab = (XmTabStackWidget) client;
    
    switch( info->reason )
    {
    case XmCR_DRAG_DROP_FINISH:
	break;
    case XmCR_DRAG_MOTION:
	break;
    case XmCR_DROP_FINISH:
	break;
    case XmCR_DROP_SITE_ENTER:
        {
	    XmDropSiteEnterCallback enter = (XmDropSiteEnterCallback)cbdata;

	    switch( enter->dropSiteStatus )
	    {
	    case XmINVALID_DROP_SITE:
	    default:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, XmTabStack__invalid_icon(tab),
			      NULL);
		break;
	    case XmVALID_DROP_SITE:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, NULL,
			      NULL);
		break;
	    }	
	}
	break;
    case XmCR_DROP_SITE_LEAVE:
	XtVaSetValues(widget,
		      XmNstateCursorIcon, XmTabStack__invalid_icon(tab),
		      NULL);
	break;
    case XmCR_DROP_START:
        {
	    XmDropSiteEnterCallback enter = (XmDropSiteEnterCallback)cbdata;

	    switch( enter->dropSiteStatus )
	    {
	    case XmINVALID_DROP_SITE:
	    default:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, XmTabStack__invalid_icon(tab),
			      NULL);
		break;
	    case XmVALID_DROP_SITE:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, NULL,
			      NULL);
		break;
	    }	
	}
	break;
    case XmCR_OPERATION_CHANGED:
        {
	    XmOperationChangedCallback change =
		(XmOperationChangedCallback) cbdata;

	    switch( change->operation )
	    {
	    case XmDROP_MOVE:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, NULL,
			      NULL);
		break;
	    default:
		XtVaSetValues(widget,
			      XmNstateCursorIcon, XmTabStack__invalid_icon(tab),
			      NULL);
		break;
	    }
	}
	break;
    case XmCR_TOP_LEVEL_ENTER:
	break;
    case XmCR_TOP_LEVEL_LEAVE:
	break;
    default:
	break;
    }
}

static void
#ifndef _NO_PROTO
XmTabStackMenu(Widget widget, XEvent *event, String *params,
	       Cardinal *num_params)
#else
XmTabStackMenu(widget, event, params, num_params)
    Widget   widget;
    XEvent   *event;
    String   *params;
    Cardinal *num_params;
#endif
{
    Widget           parent = XtParent(widget), child;
    XmTabStackWidget tab = (XmTabStackWidget)XtParent(parent);

    /*
     * Lets start by finding out if we are popping this menu up from
     * a tab.
     */
    child = XmTabStackXYToWidget((Widget) tab, event->xbutton.x,
				 event->xbutton.y);

    /*
     * If we are not popping this menu up, and we are not in a tab
     * then we want to desensitise the tear off option.
     */
    XtSetSensitive(XmTabStack__tear_off_button(tab), (child != NULL));
    XtVaSetValues(XmTabStack__tear_off_button(tab),
		  XmNuserData, child, NULL);

    XmMenuPosition(XmTabStack__menu(tab), (XButtonPressedEvent*)event);
    XtManageChild(XmTabStack__menu(tab));
}

static void
#ifndef _NO_PROTO
XmTabBoxDragTab(Widget widget, XEvent *event, String *params,
		Cardinal *num_params)
#else
XmTabBoxDragTab(widget, event, params, num_params)
    Widget   widget;
    XEvent   *event;
    String   *params;
    Cardinal *num_params;
#endif
{
    Widget           parent = XtParent(widget), drag, child, source_icon,
                     invalid_icon;
    XmTabStackWidget tab = (XmTabStackWidget)XtParent(parent);
    Arg              args[10];
    Cardinal         n = 0;
    Atom             target;
    int              idx;
    Pixmap           source_pixmap, source_mask, invalid_pixmap, invalid_mask;

#ifdef TEAR_OFF_TABS
    if( !XmTabStack_allow_tear_offs(tab) ||
        event == NULL || event->xany.type != ButtonPress ||
        (child = IndexToTab(tab,
			    XmTabBoxGetIndex(parent, event->xbutton.x,
					     event->xbutton.y))) == NULL ||
        !XmTabStackC_tear_off_enabled(child) )
    {
	return;
    }
#endif /* TEAR_OFF_TABS */

    if( XmTabStack__source_icon(tab) == NULL )
    {
	source_pixmap = XCreateBitmapFromData(XtDisplay(widget),
					      XtWindow(widget),
					      (char*)tab_pix_bits,
					      tab_pix_width,
					      tab_pix_height);
	source_mask = XCreateBitmapFromData(XtDisplay(widget),
					    XtWindow(widget),
					    (char*)tab_mask_bits,
					    tab_pix_width,
					    tab_pix_height);
	invalid_pixmap = XCreateBitmapFromData(XtDisplay(widget),
					       XtWindow(widget),
					       (char*)invalid_bits,
					       tab_pix_width,
					       tab_pix_height);
	invalid_mask = XCreateBitmapFromData(XtDisplay(widget),
					     XtWindow(widget),
					     (char*)invalid_mask_bits,
					     tab_pix_width,
					     tab_pix_height);

	XtSetArg(args[n], XmNdepth, 1); n++;
	XtSetArg(args[n], XmNwidth, tab_pix_width); n++;
	XtSetArg(args[n], XmNheight, tab_pix_height); n++;
	XtSetArg(args[n], XmNpixmap, source_pixmap); n++;
	XtSetArg(args[n], XmNmask, source_mask); n++;
	source_icon = XmCreateDragIcon(parent, "tabSourceIcon", args, n); n=0;
				       
	XtSetArg(args[n], XmNdepth, 1); n++;
	XtSetArg(args[n], XmNwidth, tab_pix_width); n++;
	XtSetArg(args[n], XmNheight, tab_pix_height); n++;
	XtSetArg(args[n], XmNpixmap, invalid_pixmap); n++;
	XtSetArg(args[n], XmNmask, invalid_mask); n++;
	invalid_icon = XmCreateDragIcon(parent, "tabInvalidIcon", args, n);n=0;

	XmTabStack__source_pixmap(tab) = source_pixmap;
	XmTabStack__source_mask(tab) = source_mask;
	XmTabStack__invalid_pixmap(tab) = invalid_pixmap;
	XmTabStack__invalid_mask(tab) = invalid_mask;
	XmTabStack__source_icon(tab) = source_icon;
	XmTabStack__invalid_icon(tab) = invalid_icon;
    }
    else
    {
	source_icon = XmTabStack__source_icon(tab);
	invalid_icon = XmTabStack__invalid_icon(tab);
    }

    target = TabAtom(widget);
    XtSetArg(args[n], XmNblendModel, XmBLEND_ALL); ++n;
    XtSetArg(args[n], XmNsourceCursorIcon, source_icon); ++n;
    XtSetArg(args[n], XmNsourcePixmapIcon, source_icon); ++n;
    XtSetArg(args[n], XmNdragOperations, XmDROP_MOVE); ++n;
    XtSetArg(args[n], XmNexportTargets, &target); ++n;
    XtSetArg(args[n], XmNnumExportTargets, 1); ++n;
    XtSetArg(args[n], XmNconvertProc, TabConvertProc); ++n;
    XtSetArg(args[n], XmNclientData, child); ++n;
    drag = XmDragStart(parent, event, args, n); n = 0;

    XtAddCallback(drag, XmNdragDropFinishCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNdragMotionCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNdropFinishCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNdropSiteEnterCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNdropSiteLeaveCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNdropStartCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNoperationChangedCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNtopLevelEnterCallback,
		  DragCallback, (XtPointer) tab);
    XtAddCallback(drag, XmNtopLevelLeaveCallback,
		  DragCallback, (XtPointer) tab);
}

#endif
