/* Copyright (c) 1999   Alexander Yukhimets. All rights reserved. */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <X11/StringDefs.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>

#include <Xm/DrawingA.h>

#include <XmAxy/NotebookP.h>
#include <XmAxy/Util.h>

#include <Xm/XmP.h>
#include <Xm/DrawP.h>

#define X(w)            (((CoreWidget)(w))->core.x)
#define Y(w)            (((CoreWidget)(w))->core.y)
#define WIDTH(w)        (((CoreWidget)(w))->core.width)
#define HEIGHT(w)       (((CoreWidget)(w))->core.height)
#define DEPTH(w)        (((CoreWidget)(w))->core.depth)
#define BORDER(w)       (((CoreWidget)(w))->core.border_width)
#define COLORMAP(w)     (((CoreWidget)(w))->core.colormap)
#define BACKGROUND(w)   (((CoreWidget)(w))->core.background_pixel)

#define NUM_CHILDREN(w)	(((CompositeWidget)(w))->composite.num_children)
#define CHILD(w,i)      (((CompositeWidget)(w))->composite.children[(i)])

#define CONSTRAINTS(w)  (((CoreWidget)(w))->core.constraints)

#define HAS_FOCUS(w)         (((XmManagerWidget)(w))->manager.has_focus)
#define SHADOW_THICKNESS(w)  (((XmManagerWidget)(w))->manager.shadow_thickness)
#define STRING_DIR(w)        (((XmManagerWidget)(w))->manager.string_direction)
#define FOREGROUND(w)        (((XmManagerWidget)(w))->manager.foreground)
#define HIGHLIGHT_COLOR(w)   (((XmManagerWidget)(w))->manager.highlight_color)
#define HIGHLIGHT_PIXMAP(w)  (((XmManagerWidget)(w))->manager.highlight_pixmap)
#define HIGHLIGHT_GC(w)      (((XmManagerWidget)(w))->manager.highlight_GC)
#define BACKGROUND_GC(w)     (((XmManagerWidget)(w))->manager.background_GC)
#define TOP_SHADOW_GC(w)     (((XmManagerWidget)(w))->manager.top_shadow_GC)
#define BOTTOM_SHADOW_GC(w)  (((XmManagerWidget)(w))->manager.bottom_shadow_GC)


#define ARM_COLOR(w)        (((XmAxyNotebookWidget)(w))->notebook.arm_color)
#define FONT_LIST(w)        (((XmAxyNotebookWidget)(w))->notebook.font_list)
#define SHOW_TABS(w)        (((XmAxyNotebookWidget)(w))->notebook.show_tabs)
#define HIGHLIGHT_THICKNESS(w) \
  (((XmAxyNotebookWidget)(w))->notebook.highlight_thickness)
#define MARGIN_WIDTH(w)     (((XmAxyNotebookWidget)(w))->notebook.margin_width)
#define MARGIN_HEIGHT(w)    (((XmAxyNotebookWidget)(w))->notebook.margin_height)
#define INNER_MARGIN_WIDTH(w) \
  (((XmAxyNotebookWidget)(w))->notebook.inner_margin_width)
#define INNER_MARGIN_HEIGHT(w) \
  (((XmAxyNotebookWidget)(w))->notebook.inner_margin_height)
#define TAB_SPACING(w)      (((XmAxyNotebookWidget)(w))->notebook.tab_spacing)
#define TAB_RAISE(w)        (((XmAxyNotebookWidget)(w))->notebook.tab_raise)
#define TAB_MARGIN_WIDTH(w) \
  (((XmAxyNotebookWidget)(w))->notebook.tab_margin_width)
#define TAB_MARGIN_HEIGHT(w) \
  (((XmAxyNotebookWidget)(w))->notebook.tab_margin_height)
#define LABEL_MARGIN_WIDTH(w) \
  (((XmAxyNotebookWidget)(w))->notebook.label_margin_width)
#define LABEL_MARGIN_HEIGHT(w) \
  (((XmAxyNotebookWidget)(w))->notebook.label_margin_height)
#define PAGE_CHANGED_CB(w) \
  (((XmAxyNotebookWidget)(w))->notebook.page_changed_callback)

#define FONT_FID(w)       (((XmAxyNotebookWidget)(w))->notebook.font_fid)
#define FONT_WIDTH(w)     (((XmAxyNotebookWidget)(w))->notebook.font_width)
#define FONT_HEIGHT(w)    (((XmAxyNotebookWidget)(w))->notebook.font_height)
#define FONT_Y(w)         (((XmAxyNotebookWidget)(w))->notebook.font_y)
#define DRAWING_AREA(w)   (((XmAxyNotebookWidget)(w))->notebook.drawing_area)
#define HIGHLIGHTED_TAB(w) \
  (((XmAxyNotebookWidget)(w))->notebook.highlighted_tab)
#define HIGHLIGHT_DRAWN(w) \
  (((XmAxyNotebookWidget)(w))->notebook.highlight_drawn)
#define CURRENT_WIDGET(w) (((XmAxyNotebookWidget)(w))->notebook.current_widget)
#define CURRENT_PAGE(w)   (((XmAxyNotebookWidget)(w))->notebook.current_page)
#define NUM_PAGES(w)      (((XmAxyNotebookWidget)(w))->notebook.num_pages)
#define TAB_HEIGHT(w)     (((XmAxyNotebookWidget)(w))->notebook.tab_height)
#define CHILD_X(w)        (((XmAxyNotebookWidget)(w))->notebook.child_x)
#define CHILD_Y(w)        (((XmAxyNotebookWidget)(w))->notebook.child_y)
#define CHILD_WIDTH(w)    (((XmAxyNotebookWidget)(w))->notebook.child_width)
#define CHILD_HEIGHT(w)	  (((XmAxyNotebookWidget)(w))->notebook.child_height)
#define NORMAL_GC(w)      (((XmAxyNotebookWidget)(w))->notebook.normal_gc)
#define ARM_GC(w)         (((XmAxyNotebookWidget)(w))->notebook.arm_gc)
#define TAB_CHILD(w)      (((XmAxyNotebookWidget)(w))->notebook.tab_child)

#define CHILD_CONSTR(w,i) CONSTRAINTS(CHILD(w,i))
#define C_TAB_LABEL(r) (((XmAxyNotebookConstraintRec*)r)->notebook.tab_label)
#define C_RESIZABLE(r) (((XmAxyNotebookConstraintRec*)r)->notebook.resizable)
#define C_TAB_RECT(r)  (((XmAxyNotebookConstraintRec*)r)->notebook.tab_rect)
#define C_PAGE_NUM(r)  (((XmAxyNotebookConstraintRec*)r)->notebook.page_number)
#define C_IS_TAB(r)    (((XmAxyNotebookConstraintRec*)r)->notebook.is_tab)
#define C_TAB_CLIP(r)  (((XmAxyNotebookConstraintRec*)r)->notebook.tab_clip)
#define C_TAB_VISIBLE(r) \
  (((XmAxyNotebookConstraintRec*)r)->notebook.tab_visible)

#define TAB_LABEL_I(w,i)   C_TAB_LABEL(CHILD_CONSTR(w,i))
#define RESIZABLE_I(w,i)   C_RESIZABLE(CHILD_CONSTR(w,i))
#define TAB_RECT_I(w,i)    C_TAB_RECT(CHILD_CONSTR(w,i))
#define PAGE_NUM_I(w,i)    C_PAGE_NUM(CHILD_CONSTR(w,i))
#define IS_TAB_I(w,i)      C_IS_TAB(CHILD_CONSTR(w,i))
#define TAB_CLIP_I(w,i)    C_TAB_CLIP(CHILD_CONSTR(w,i))
#define TAB_VISIBLE_I(w,i) C_TAB_VISIBLE(CHILD_CONSTR(w,i))

#define TAB_LABEL(c)       C_TAB_LABEL(CONSTRAINTS(c))
#define RESIZABLE(c)       C_RESIZABLE(CONSTRAINTS(c))
#define TAB_RECT(c)        C_TAB_RECT(CONSTRAINTS(c))
#define PAGE_NUM(c)        C_PAGE_NUM(CONSTRAINTS(c))
#define IS_TAB(c)          C_IS_TAB(CONSTRAINTS(c))
#define TAB_CLIP(c)        C_TAB_CLIP(CONSTRAINTS(c))
#define TAB_VISIBLE(c)     C_TAB_VISIBLE(CONSTRAINTS(c))

#define DEFAULT_FONT_LIST(w) _XmGetDefaultFontList((Widget)w, XmFONT_IS_FONT)
#define EHT(w) \
  ((HIGHLIGHT_THICKNESS(w)<=0)?(Dimension)0:(HIGHLIGHT_THICKNESS(w)+1))


/* class methods */
static void Initialize(Widget,Widget,ArgList,Cardinal*);
static void Destroy(Widget);
static void Resize(Widget);
static void Redisplay(Widget, XEvent*, Region);
static Boolean SetValues(Widget,Widget, Widget,ArgList,Cardinal*);
static XtGeometryResult QueryGeometry(Widget,XtWidgetGeometry*,
                                    XtWidgetGeometry*);

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

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

/* action routines */
static void DAFocusIn(Widget,XEvent*,String*,Cardinal*);
static void DAFocusOut(Widget,XEvent*,String*,Cardinal*);
static void DAMouseDown(Widget,XEvent*,String*,Cardinal*);
static void DAMoveLeft(Widget,XEvent*,String*,Cardinal*);
static void DAMoveRight(Widget,XEvent*,String*,Cardinal*);
static void DASelect(Widget,XEvent*,String*,Cardinal*);

/* local functions */
static void install_font(Widget);
static void create_gc(Widget);
static void calc_preferred_size(Widget,Dimension*,Dimension*,Widget);
static void do_layout(Widget,Widget);
static void compute_tab_rects(Widget);
static void configure_children(Widget,Widget);
static void draw_shadows(Widget,Region);
static void draw_da_tabs(Widget,Window,XEvent*);
static void draw_da_highlight(Widget);
static Widget search_da_tab(Widget, XEvent*);
static void expose_da_cb(Widget,XtPointer,XtPointer);
static void change_tab(Widget, Widget);

static XtActionsRec actions[] = {
  {"XmAxyNotebookDAMouseDown",DAMouseDown},
  {"XmAxyNotebookDAFocusIn",DAFocusIn},
  {"XmAxyNotebookDAFocusOut",DAFocusOut},
  {"XmAxyNotebookDAMoveRight",DAMoveRight},
  {"XmAxyNotebookDAMoveLeft",DAMoveLeft},
  {"XmAxyNotebookDASelect",DASelect},
};

static char drawingAreaTranslations[]="\
<FocusIn>:		XmAxyNotebookDAFocusIn()\n\
<FocusOut>:		XmAxyNotebookDAFocusOut()\n\
<Btn1Down>:		XmAxyNotebookDAMouseDown()\n\
<Btn1Down>(2+):		XmAxyNotebookDAMouseDown()\n\
:<Key>osfRight:		XmAxyNotebookDAMoveRight()\n\
:<Key>osfLeft:		XmAxyNotebookDAMoveLeft()\n\
:<Key>osfDown:		XmAxyNotebookDAMoveRight()\n\
:<Key>osfUp:		XmAxyNotebookDAMoveLeft()\n\
<Key>Return:		XmAxyNotebookDASelect()\n\
<Key>space:		XmAxyNotebookDASelect()\n\
";

#define Offset(field) XtOffsetOf(XmAxyNotebookRec, notebook.field)
#define offset(field) XtOffsetOf(XmAxyNotebookRec, field)
static XtResource resources[]={

  {XmNselectColor,XmCSelectColor,
    XmRPixel,sizeof(Pixel),
    Offset(arm_color),XmRImmediate,
    (XtPointer)XmUNSPECIFIED_PIXEL},

  {XmNfontList,XmCFontList,
    XmRFontList,sizeof(XmFontList),
    Offset(font_list),XmRImmediate,
    (XtPointer)NULL},

  {XmNshowTabs,XmCShowTabs,
    XmRBoolean,sizeof(Boolean),
    Offset(show_tabs),XmRImmediate,
    (XtPointer)TRUE},

  {XmNhighlightThickness,XmCHighlightThickness,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(highlight_thickness),XmRImmediate,
    (XtPointer)1},

  {XmNinnerMarginHeight,XmCInnerMarginHeight,
    XmRVerticalDimension,sizeof(Dimension),
    Offset(inner_margin_height),XmRImmediate,
    (XtPointer)4},

  {XmNinnerMarginWidth,XmCInnerMarginWidth,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(inner_margin_width),XmRImmediate,
    (XtPointer)4},

  {XmNmarginHeight,XmCMarginHeight,
    XmRVerticalDimension,sizeof(Dimension),
    Offset(margin_height),XmRImmediate,
    (XtPointer)6},

  {XmNmarginWidth,XmCMarginWidth,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(margin_width),XmRImmediate,
    (XtPointer)6},

  {XmNpageChangedCallback,XmCPageChangedCallback,
    XtRCallback,sizeof(XtPointer),
    Offset(page_changed_callback), XtRImmediate,
    (XtPointer)NULL},

  {XmNtabRaise,XmCTabRaise,
    XmRVerticalDimension,sizeof(Dimension),
    Offset(tab_raise), XtRImmediate,
    (XtPointer)2},

  {XmNtabSpacing,XmCTabSpacing,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(tab_spacing),XmRImmediate,
    (XtPointer)0},

  {XmNtabMarginWidth,XmCTabMarginWidth,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(tab_margin_width),XmRImmediate,
    (XtPointer)2},

  {XmNtabMarginHeight,XmCTabMarginHeight,
    XmRVerticalDimension,sizeof(Dimension),
    Offset(tab_margin_height),XmRImmediate,
    (XtPointer)2},

  {XmNlabelMarginWidth,XmCLabelMarginWidth,
    XmRHorizontalDimension,sizeof(Dimension),
    Offset(label_margin_width),XmRImmediate,
    (XtPointer)2},

  {XmNlabelMarginHeight,XmCLabelMarginHeight,
    XmRVerticalDimension,sizeof(Dimension),
    Offset(label_margin_height),XmRImmediate,
    (XtPointer)2},

  {XmNshadowThickness,XmCShadowThickness,
    XmRHorizontalDimension,sizeof(Dimension),
    offset(manager.shadow_thickness),XmRImmediate,
    (XtPointer)2},

};

static XmSyntheticResource syn_resources[] = {

    {XmNhighlightThickness, sizeof(Dimension), Offset(highlight_thickness),
      _XmFromHorizontalPixels, _XmToHorizontalPixels},

    {XmNinnerMarginWidth, sizeof(Dimension), Offset(inner_margin_width),
      _XmFromHorizontalPixels, _XmToHorizontalPixels},

    {XmNinnerMarginHeight, sizeof(Dimension), Offset(inner_margin_height),
      _XmFromVerticalPixels, _XmToVerticalPixels},

    {XmNmarginWidth, sizeof(Dimension), Offset(margin_width),
      _XmFromHorizontalPixels, _XmToHorizontalPixels},

    {XmNmarginHeight, sizeof(Dimension), Offset(margin_height),
      _XmFromVerticalPixels, _XmToVerticalPixels},

    {XmNtabRaise, sizeof(Dimension), Offset(tab_raise),
      _XmFromVerticalPixels, _XmToVerticalPixels},

    {XmNtabMarginWidth, sizeof(Dimension), Offset(tab_margin_width),
      _XmFromHorizontalPixels, _XmToHorizontalPixels},

    {XmNtabMarginHeight, sizeof(Dimension), Offset(tab_margin_height),
      _XmFromVerticalPixels, _XmToVerticalPixels},

    {XmNlabelMarginWidth, sizeof(Dimension), Offset(label_margin_width),
      _XmFromHorizontalPixels, _XmToHorizontalPixels},

    {XmNlabelMarginHeight, sizeof(Dimension), Offset(label_margin_height),
      _XmFromVerticalPixels, _XmToVerticalPixels},

};      
#undef offset
#undef Offset

static XtResource constraints[]={

  {XmNtabLabel,XmCTabLabel,
    XmRXmString,sizeof(XmString),
    XtOffsetOf(XmAxyNotebookConstraintRec, notebook.tab_label),
    XmRImmediate,(XtPointer)NULL},

  {XmNresizable,XmCResizable,
    XmRBoolean,sizeof(Boolean),
    XtOffsetOf(XmAxyNotebookConstraintRec, notebook.resizable),
    XmRImmediate,(XtPointer)TRUE}

};


externaldef (xmaxynotebookclassrec) 
            XmAxyNotebookClassRec xmAxyNotebookClassRec={
    /* Core class part */
    {
      /* superclass */                  (WidgetClass)&xmManagerClassRec,
      /* class_name */                  "XmAxyNotebook",
      /* widget_size */                 sizeof(XmAxyNotebookRec),
      /* class_initialize */            NULL,
      /* class_part_initialize */       NULL,
      /* class_inited */                FALSE,
      /* initialize */                  Initialize,
      /* initialize_hook */             NULL,
      /* realize */                     XtInheritRealize,
      /* actions */                     actions,
      /* num_actions */                 XtNumber(actions),
      /* resources */                   resources,
      /* num_resources */               XtNumber(resources),
      /* xrm_class */                   NULLQUARK,
      /* compress_motion */             TRUE,
      /* compress_exposure */           XtExposeCompressMaximal,
      /* compress_enterleave */         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_private */            NULL,
      /* tm_table */                    XtInheritTranslations,
      /* query_geometry */              QueryGeometry,
      /* display_accelerator */         NULL,
      /* extension */                   NULL,
    },
    /* Composite Class part */
    {
      /* geometry_manager */            GeometryManager,
      /* change_managed */              ChangeManaged,
      /* insert_child */                XtInheritInsertChild,
      /* delete_child */                XtInheritDeleteChild,
      /* extension */                   NULL,
    },
    /* Constraint Class part */
    {
      /* resources */                   constraints,
      /* num_resources */               XtNumber(constraints),
      /* constraint_size */             sizeof(XmAxyNotebookConstraintRec),
      /* initialize */                  ConstraintInitialize,
      /* destroy */                     ConstraintDestroy,
      /* set_values */                  ConstraintSetValues,
      /* extension */                   NULL,
    },
    /* Manager Class part */
    {
      /* translations */                XtInheritTranslations,
      /* syn_resources */               syn_resources,
      /* num_syn_resources */           XtNumber(syn_resources),
      /* syn_constraint_resources */    NULL,
      /* num_syn_constraint_resources */0,
      /* parent_process */              XmInheritParentProcess,
      /* extension */                   NULL,
    },
    /* Notebook Class part */
    {
      /* extension */                   NULL,
    }
};

externaldef (xmaxynotebookwidgetclass) 
  WidgetClass xmAxyNotebookWidgetClass=(WidgetClass) &xmAxyNotebookClassRec;



/* Core class methods */

static void Initialize(Widget wreq,Widget wnew,ArgList args,Cardinal *nargs){
  Arg arg[10];
  Cardinal n;

  if(ARM_COLOR(wnew)==XmUNSPECIFIED_PIXEL){
    Pixel fg,ts,bs;
    XmGetColors(XtScreen(wnew),COLORMAP(wnew),BACKGROUND(wnew),
	&fg,&ts,&bs,&ARM_COLOR(wnew));
  }
  if(FONT_LIST(wnew)==NULL){
    FONT_LIST(wnew)=XmFontListCopy(
	_XmGetDefaultFontList(wnew,XmLABEL_FONTLIST));
  } else {
    FONT_LIST(wnew)=XmFontListCopy(FONT_LIST(wnew));
  }
  install_font(wnew);
  CURRENT_WIDGET(wnew)=NULL;
  CURRENT_PAGE(wnew)=0;
  NUM_PAGES(wnew)=0;
  TAB_HEIGHT(wnew)=0;
  CHILD_WIDTH(wnew)=0;
  CHILD_HEIGHT(wnew)=0;

  HIGHLIGHTED_TAB(wnew)=0;
  HIGHLIGHT_DRAWN(wnew)=FALSE;

  create_gc(wnew);

  TAB_CHILD(wnew)=TRUE;
  n=0;
  XtSetArg(arg[n],XmNbackground,BACKGROUND(wnew));n++;
  DRAWING_AREA(wnew)=XtCreateWidget("Tabs",xmDrawingAreaWidgetClass,
      wnew,arg,n);

  XtOverrideTranslations(DRAWING_AREA(wnew),
      XtParseTranslationTable(drawingAreaTranslations));
  if(SHOW_TABS(wnew))XtManageChild(DRAWING_AREA(wnew));
  XtAddCallback(DRAWING_AREA(wnew),XmNexposeCallback,
      expose_da_cb,(XtPointer)wnew);
  XtAddCallback(DRAWING_AREA(wnew),XmNresizeCallback,
      expose_da_cb,(XtPointer)wnew);

  XtVaSetValues(wnew,XmNinitialFocus,DRAWING_AREA(wnew),NULL);
}

static void Destroy(Widget w){
  XmFontListFree(FONT_LIST(w));
  XtReleaseGC(w,NORMAL_GC(w));
  XtReleaseGC(w,ARM_GC(w));
}

static void Resize(Widget w){
  do_layout(w,NULL);
  draw_shadows(w,NULL);
}

static void Redisplay(Widget w, XEvent *event, Region region){
  draw_shadows(w,region);
  _XmRedisplayGadgets(w,event,region);
}

#define NE(field) \
(((XmAxyNotebookWidget)wcur)->field != ((XmAxyNotebookWidget)wnew)->field)
#define NB(field) \
((((XmAxyNotebookWidget)wcur)->field && !((XmAxyNotebookWidget)wnew)->field) ||\
(!((XmAxyNotebookWidget)wcur)->field && ((XmAxyNotebookWidget)wnew)->field))
static Boolean SetValues(Widget wcur,Widget wreq, Widget wnew,
				ArgList args,Cardinal *nargs){
  Boolean redraw=FALSE;
  Boolean layout=FALSE;

  if(NE(notebook.arm_color) || NE(manager.foreground) || 
					NE(core.background_pixel)){
    XtReleaseGC(wnew,NORMAL_GC(wnew));
    XtReleaseGC(wnew,ARM_GC(wnew));
    create_gc(wnew);
    XtVaSetValues(DRAWING_AREA(wnew),XmNbackground,BACKGROUND(wnew),NULL);
    redraw=TRUE;
  }
  if(NE(notebook.font_list)){
    XmFontListFree(FONT_LIST(wcur));
    install_font(wnew);
    layout=TRUE;
    redraw=TRUE;
  }
  if(NE(notebook.tab_raise)){
    layout=TRUE;
    redraw=TRUE;
  }
  if(NB(notebook.show_tabs)){
    layout=TRUE;
    redraw=TRUE;
    if(!SHOW_TABS(wnew))XtUnmanageChild(DRAWING_AREA(wnew));
    else XtManageChild(DRAWING_AREA(wnew));
  }
  if(NE(notebook.highlight_thickness) || NE(notebook.margin_width) ||
     NE(notebook.margin_height) || NE(notebook.inner_margin_height) ||
     NE(notebook.inner_margin_width) || NE(notebook.tab_spacing) ||
     NE(manager.shadow_thickness) || NE(manager.string_direction)){
    layout=TRUE;
    redraw=TRUE;
  }

  if(layout) {
    if(HEIGHT(wcur)==HEIGHT(wnew) || WIDTH(wcur)==WIDTH(wnew)){
      Dimension width,height;
      calc_preferred_size(wnew,&width,&height,NULL);
      if(HEIGHT(wcur)==HEIGHT(wnew)) HEIGHT(wnew)=height;
      if(WIDTH(wcur)==WIDTH(wnew)) WIDTH(wnew)=width;
    }
    if(HEIGHT(wcur)==HEIGHT(wnew) && WIDTH(wcur)==WIDTH(wnew)){
      do_layout(wnew,NULL);
      redraw=TRUE;
    }
  }

  return redraw;
}
#undef NE
#undef NB

static XtGeometryResult QueryGeometry(Widget w, 
			XtWidgetGeometry *proposed,XtWidgetGeometry *answer){
  Dimension width,height;

  calc_preferred_size(w,&width,&height,NULL);
  if(!XtIsRealized(w)){
    if(WIDTH(w)!=0) width=WIDTH(w);
    if(HEIGHT(w)!=0) height=HEIGHT(w);
  }
  if(answer!=NULL){
    answer->request_mode=(CWWidth | CWHeight);
    answer->width=width;
    answer->height=height;
  }

  if(((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
    && proposed->width==width && proposed->height==height)
    return XtGeometryYes;
  else if(width == WIDTH(w) && height == HEIGHT(w))
    return XtGeometryNo;
  else
    return XtGeometryAlmost;
}


/* Composite methods */

static XtGeometryResult GeometryManager(Widget child, 
			XtWidgetGeometry *request,XtWidgetGeometry *reply){
  Widget w=XtParent(child);
  Dimension width,height,saved_width,saved_height;
  XtWidgetGeometry parent;
  XtGeometryResult result;

  if(!RESIZABLE(child)) 
    return XtGeometryNo;


  if((request->request_mode & CWX) || (request->request_mode & CWY))
    return XtGeometryNo;

  saved_width=WIDTH(child);
  saved_height=HEIGHT(child);
  WIDTH(child)=request->width;
  HEIGHT(child)=request->height;
  calc_preferred_size(w,&width,&height,child);
  WIDTH(child)=saved_width;
  HEIGHT(child)=saved_height;

  if(width==WIDTH(w) && height==HEIGHT(w))
    return XtGeometryYes;

  parent.request_mode=(CWWidth|CWHeight);
  parent.width=width;
  parent.height=height;
  if(request->request_mode & XtCWQueryOnly){
    parent.request_mode |= XtCWQueryOnly;
  }
  result=XtMakeGeometryRequest(XtParent(w),&parent,NULL);
  if(result==XtGeometryAlmost) result=XtGeometryNo;

  if(result==XtGeometryYes && !(request->request_mode & XtCWQueryOnly)){
    WIDTH(child)=request->width;
    HEIGHT(child)=request->height;
    do_layout(w,child);
  } else {
    do_layout(w,child);
  }

  return result;
}

static void ChangeManaged(Widget w){
  Dimension width,height;
  int i,page=0;

  for(i=0;i<NUM_CHILDREN(w);i++){
    if(CHILD(w,i)==DRAWING_AREA(w)) continue;
    if(XtIsManaged(CHILD(w,i))) {
      PAGE_NUM_I(w,i)=(++page);
      if(CURRENT_WIDGET(w)==CHILD(w,i)) CURRENT_PAGE(w)=page;
      else if(page>0 && CURRENT_PAGE(w)==page) CURRENT_WIDGET(w)=CHILD(w,i);
    }
  }
  if(CURRENT_WIDGET(w)==NULL || PAGE_NUM(CURRENT_WIDGET(w))!=CURRENT_PAGE(w)){
    for(i=0;i<NUM_CHILDREN(w);i++){
      if(CHILD(w,i)==DRAWING_AREA(w)) continue;
      if(XtIsManaged(CHILD(w,i))){
	CURRENT_PAGE(w)=PAGE_NUM_I(w,i);
	CURRENT_WIDGET(w)=CHILD(w,i);
	break;
      }
    }
  }
  NUM_PAGES(w)=page;
  calc_preferred_size(w,&width,&height,NULL);
  if(!XtIsRealized(w)) {
    if(WIDTH(w)!=0) width=WIDTH(w);
    if(HEIGHT(w)!=0) height=HEIGHT(w);
  }
  while(XtMakeResizeRequest(w,width,height,&width,&height)==XtGeometryAlmost);
  do_layout(w,NULL);
  _XmNavigChangeManaged(w);
}


/* Constraint class methods */

static void ConstraintInitialize(Widget treq,Widget tnew,
			ArgList args,Cardinal *nargs){
  Widget w=XtParent(tnew);

  if(TAB_CHILD(w)){
    TAB_LABEL(tnew)=NULL;
    PAGE_NUM(tnew)=0;
    IS_TAB(tnew)=TRUE;
    TAB_CHILD(w)=FALSE;
  } else {
    if(TAB_LABEL(tnew)){
      TAB_LABEL(tnew)=XmStringCopy(TAB_LABEL(tnew));
    } else {
      TAB_LABEL(tnew)=XmStringCreateLocalized(XtName(tnew));
    }
    PAGE_NUM(tnew)=0;
    IS_TAB(tnew)=FALSE;
  }
}

static void ConstraintDestroy(Widget w){
  if(TAB_LABEL(w))XmStringFree(TAB_LABEL(w));
}

static Boolean ConstraintSetValues(Widget wcur,Widget wreq, Widget wnew,
                                          ArgList args,Cardinal *nargs){
  Boolean redraw = FALSE; 
  Boolean layout = FALSE; 
  Widget w=XtParent(wnew);

  if(!XmStringCompare(TAB_LABEL(wcur),TAB_LABEL(wnew))){
    XmStringFree(TAB_LABEL(wcur));
    TAB_LABEL(wnew)=XmStringCopy(TAB_LABEL(wnew));
    layout=TRUE;
    redraw = TRUE;
  }

  if(layout) {
    Dimension width,height;
    calc_preferred_size(w,&width,&height,NULL);
    HEIGHT(wnew)=height;
    WIDTH(wnew)=width;
    do_layout(wnew,NULL);
    redraw=TRUE;
  }

  return(redraw);
}


/* Actions */

static void DAFocusIn(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);
  if(!CURRENT_WIDGET(w)) return;
  HIGHLIGHTED_TAB(w)=CURRENT_WIDGET(w);
  HIGHLIGHT_DRAWN(w)=TRUE;
  draw_da_highlight(da);
}

static void DAFocusOut(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);
  if(!HIGHLIGHTED_TAB(w) || !HIGHLIGHT_DRAWN(w)) return;
  HIGHLIGHT_DRAWN(w)=FALSE;
  draw_da_highlight(da);
  HIGHLIGHTED_TAB(w)=0;
}

static void DAMouseDown(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);
  Widget child=search_da_tab(da,event);
  XmAxyNotebookCallbackStruct cbs;
  XmString prev,curr;

  if(!child) return;
  XmProcessTraversal(da,XmTRAVERSE_CURRENT);
  if(child==CURRENT_WIDGET(w)) return;

  cbs.reason=XmAxyNotebookPageChangedReason;
  cbs.event=event;

  cbs.prev_page_number=CURRENT_PAGE(w);
  cbs.prev_page_widget=CURRENT_WIDGET(w);
  cbs.prev_tab_label=prev=XmStringCopy(TAB_LABEL(CURRENT_WIDGET(w)));

  change_tab(w,child);

  cbs.page_number=CURRENT_PAGE(w);
  cbs.page_widget=CURRENT_WIDGET(w);
  cbs.tab_label=curr=XmStringCopy(TAB_LABEL(CURRENT_WIDGET(w)));

  XtCallCallbackList(w,PAGE_CHANGED_CB(w),&cbs);

  XmStringFree(prev);
  XmStringFree(curr);
}

static void DAMoveLeft(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);
  int i,page;

  if(!HIGHLIGHTED_TAB(w)) return;

  if(PAGE_NUM(HIGHLIGHTED_TAB(w))==1) page=NUM_PAGES(w);
  else page=PAGE_NUM(HIGHLIGHTED_TAB(w))-1;

  for(i=0;i<NUM_CHILDREN(w);i++){
    if(PAGE_NUM_I(w,i)==page){
      if(HIGHLIGHT_DRAWN(w) && HIGHLIGHTED_TAB(w)){
	HIGHLIGHT_DRAWN(w)=FALSE;
	draw_da_highlight(DRAWING_AREA(w));
      }
      HIGHLIGHTED_TAB(w)=CHILD(w,i);
      HIGHLIGHT_DRAWN(w)=TRUE;
      draw_da_highlight(DRAWING_AREA(w));
      return;
    }
  }
}

static void DAMoveRight(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);
  int i,page;

  if(!HIGHLIGHTED_TAB(w)) return;

  if(PAGE_NUM(HIGHLIGHTED_TAB(w))==NUM_PAGES(w)) page=1;
  else page=PAGE_NUM(HIGHLIGHTED_TAB(w))+1;

  for(i=0;i<NUM_CHILDREN(w);i++){
    if(/*TAB_VISIBLE_I(w,i) && */PAGE_NUM_I(w,i)==page){
      if(HIGHLIGHT_DRAWN(w) && HIGHLIGHTED_TAB(w)){
	HIGHLIGHT_DRAWN(w)=FALSE;
	draw_da_highlight(DRAWING_AREA(w));
      }
      HIGHLIGHTED_TAB(w)=CHILD(w,i);
      HIGHLIGHT_DRAWN(w)=TRUE;
      draw_da_highlight(DRAWING_AREA(w));
      return;
    }
  }
}

static void DASelect(Widget da,XEvent *event,
                        String *params,Cardinal *nparams){
  Widget w=XtParent(da);

  if(HIGHLIGHTED_TAB(w)) change_tab(w,HIGHLIGHTED_TAB(w));
}


/* public methods */

extern void XmAxyNotebookSetCurrentPage(Widget w,int page,Boolean notify){
  int i;
#ifdef XTHREADS
  XtAppContext app=XtWidgetToApplicationContext(w);
  XtAppLock(app);
#endif

  if(page<=0) {
#ifdef XTHREADS
    XtAppUnlock(app);
#endif
    return;
  }

  for(i=0;i<NUM_CHILDREN(w);i++){
    if(XtIsManaged(CHILD(w,i)) && PAGE_NUM_I(w,i)==page) {
      XmAxyNotebookSetCurrentWidget(w,CHILD(w,i),notify);
#ifdef XTHREADS
      XtAppUnlock(app);
#endif
      return;
    }
  }

  if(!XtIsRealized(w)){
    CURRENT_PAGE(w)=page;
    CURRENT_WIDGET(w)=NULL;
#ifdef XTHREADS
    XtAppUnlock(app);
#endif
    return;
  }
#ifdef XTHREADS
  XtAppUnlock(app);
#endif
}

extern Widget XmAxyNotebookGetCurrentWidget(Widget w){
  Widget cw;
#ifdef XTHREADS
  XtAppContext app=XtWidgetToApplicationContext(w);
  XtAppLock(app);
#endif

  cw=CURRENT_WIDGET(w);

#ifdef XTHREADS
  XtAppUnlock(app);
#endif

  return cw;
}

extern void XmAxyNotebookSetCurrentWidget(Widget w,Widget child,Boolean notify){
  XmAxyNotebookCallbackStruct cbs;
  XmString prev=NULL,curr;
#ifdef XTHREADS
  XtAppContext app=XtWidgetToApplicationContext(w);
  XtAppLock(app);
#endif

  if(XtParent(child)!=w || !XtIsManaged(child)) {
#ifdef XTHREADS
    XtAppUnlock(app);
#endif
    return;
  }

  if(!XtIsRealized(w)){
    CURRENT_WIDGET(w)=child;
    CURRENT_PAGE(w)=0;
#ifdef XTHREADS
    XtAppUnlock(app);
#endif
    return;
  }

  if(notify){
    cbs.reason=XmAxyNotebookPageChangedReason;
    cbs.event=NULL;

    cbs.prev_page_number=CURRENT_PAGE(w);
    cbs.prev_page_widget=CURRENT_WIDGET(w);
    cbs.prev_tab_label=prev=XmStringCopy(TAB_LABEL(CURRENT_WIDGET(w)));
  }

  change_tab(w,child);

  if(notify){
    cbs.page_number=CURRENT_PAGE(w);
    cbs.page_widget=CURRENT_WIDGET(w);
    cbs.tab_label=curr=XmStringCopy(TAB_LABEL(CURRENT_WIDGET(w)));

    XtCallCallbackList(w,PAGE_CHANGED_CB(w),&cbs);

    XmStringFree(prev);
    XmStringFree(curr);
  }

#ifdef XTHREADS
  XtAppUnlock(app);
#endif
}

extern int XmAxyNotebookGetCurrentPage(Widget w){
  int cp;
#ifdef XTHREADS
  XtAppContext app=XtWidgetToApplicationContext(w);
  XtAppLock(app);
#endif

  cp=CURRENT_PAGE(w);

#ifdef XTHREADS
  XtAppUnlock(app);
#endif

  return cp;
}

extern int XmAxyNotebookGetNumberOfPages(Widget w){
  int tp;
#ifdef XTHREADS
  XtAppContext app=XtWidgetToApplicationContext(w);
  XtAppLock(app);
#endif

  tp=NUM_PAGES(w);

#ifdef XTHREADS
  XtAppUnlock(app);
#endif

  return tp;
}

extern Widget XmAxyCreateNotebook(Widget parent,String name,ArgList args,
                                                                 Cardinal n){
  return XtCreateWidget(name,xmAxyNotebookWidgetClass,parent,args,n);
}


/* local functions */

static void install_font(Widget w){
  XmFontContext context;
  XFontStruct *font;
  XmFontListEntry font_list_entry;
  XmFontType type;
  XFontSetExtents *extents;
  XFontStruct **fonts;
  char **font_names;

  XmFontListInitFontContext(&context, FONT_LIST(w));
  font_list_entry = XmFontListNextEntry(context);
  font = (XFontStruct*)XmFontListEntryGetFont(font_list_entry, &type);
  if (type == XmFONT_IS_FONTSET){
      extents = XExtentsOfFontSet((XFontSet)font);
      FONT_WIDTH(w) = extents->max_logical_extent.width;
      FONT_HEIGHT(w) = extents->max_logical_extent.height;
      FONT_Y(w) = extents->max_logical_extent.y;
      XFontsOfFontSet((XFontSet)font, &fonts, &font_names);
      FONT_FID(w) = fonts[0]->fid;
  } else {
      FONT_WIDTH(w) = 
	(font->max_bounds.width + font->min_bounds.width) /2;
      FONT_HEIGHT(w) = 
	  (font->max_bounds.descent + font->max_bounds.ascent);
      FONT_Y(w) = font->max_bounds.ascent;
      FONT_FID(w) = font->fid;
  }
  XmFontListFreeFontContext(context);
}

static void create_gc(Widget w){
  XGCValues values;

 
  values.foreground = FOREGROUND(w);
  values.background = BACKGROUND(w);
  values.font=FONT_FID(w);

  NORMAL_GC(w) = XtAllocateGC(w,DEPTH(w),
   GCForeground|GCBackground|GCFont,&values,
   GCClipXOrigin|GCClipYOrigin|GCPlaneMask|GCForeground|GCBackground|GCFont,
   GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle|GCGraphicsExposures|
   GCDashOffset|GCArcMode);

  values.foreground=ARM_COLOR(w);

  ARM_GC(w) = XtAllocateGC(w,DEPTH(w),
   GCForeground|GCBackground,&values,
   GCForeground|GCBackground|GCClipXOrigin|GCClipYOrigin|GCPlaneMask,
   GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle|GCGraphicsExposures|
   GCDashOffset|GCArcMode);
   
}

static void calc_preferred_size(Widget w,Dimension *width,Dimension *height,
                                                                 Widget in){
  Dimension tab_height=0,tab_width=0,page_height=0,page_width=0;
  Dimension str_width,str_height;
  int i;
  Widget child;
  XtWidgetGeometry request,reply;

  for(i=0;i<NUM_CHILDREN(w);i++){
    child=CHILD(w,i);
    if(!XtIsManaged(child)) continue;

    if(SHOW_TABS(w)){
      XmStringExtent(FONT_LIST(w),TAB_LABEL(child),&str_width,&str_height);
      tab_width+=(Dimension)(str_width+SHADOW_THICKNESS(w)*2+
	  TAB_MARGIN_WIDTH(w)*2+(HIGHLIGHT_THICKNESS(w)+1)*2+
	  LABEL_MARGIN_WIDTH(w)*2+TAB_SPACING(w));
      if(str_height>tab_height) tab_height=str_height;
    }

    if(child==in){
      if(WIDTH(child)>page_width) page_width=WIDTH(child);
      if(HEIGHT(child)>page_height) page_height=HEIGHT(child);
    } else {
      request.request_mode=(CWWidth | CWHeight);
      request.width=0;
      request.height=0;
      XtQueryGeometry(child,NULL,&reply);

      if(!(reply.request_mode & CWWidth)) reply.width=WIDTH(child);
      if(!(reply.request_mode & CWHeight)) reply.height=HEIGHT(child);

      if(reply.width>page_width) page_width=reply.width;
      if(reply.height>page_height) page_height=reply.height;
    }
  }
  if(SHOW_TABS(w)){
    tab_height+=(Dimension)(SHADOW_THICKNESS(w)+TAB_MARGIN_HEIGHT(w)*2+
	EHT(w)*2+LABEL_MARGIN_HEIGHT(w)*2+TAB_RAISE(w));
    tab_width-=(Dimension)(TAB_SPACING(w));
  }
  tab_width=Max(tab_width,page_width+INNER_MARGIN_WIDTH(w)*2+
      SHADOW_THICKNESS(w)*2);
  *width=(Dimension)(tab_width+MARGIN_WIDTH(w)*2);
  *height=(Dimension)(page_height+SHADOW_THICKNESS(w)*2+tab_height+
    INNER_MARGIN_HEIGHT(w)*2+MARGIN_HEIGHT(w)*2);
}

static void do_layout(Widget w,Widget instigator){
  compute_tab_rects(w);
  configure_children(w,instigator);
}

static void compute_tab_rects(Widget w){
  Position left_x;
  Dimension greater_height,str_width,str_height;
  Widget child=0,prev=0;
  int i;
  Dimension width, height;

  left_x=0;
  greater_height = 0;

  if(SHOW_TABS(w)){
    TAB_RECT(DRAWING_AREA(w)).x=MARGIN_WIDTH(w);
    TAB_RECT(DRAWING_AREA(w)).y=MARGIN_HEIGHT(w);
    TAB_RECT(DRAWING_AREA(w)).width=i=WIDTH(w)-MARGIN_WIDTH(w)*2;
    if(i<=0) TAB_RECT(DRAWING_AREA(w)).width=1;

    for(i=0;i<NUM_CHILDREN(w);i++){
      child=CHILD(w,i);
      if(child==DRAWING_AREA(w)) continue;
      if(!XtIsManaged(child)) continue;

      XmStringExtent(FONT_LIST(w),TAB_LABEL(child),
	  &str_width,&str_height);
      width=(Dimension)(str_width+SHADOW_THICKNESS(w)*2+
	  TAB_MARGIN_WIDTH(w)*2+EHT(w)*2+LABEL_MARGIN_WIDTH(w)*2);
      height=(Dimension)(str_height+SHADOW_THICKNESS(w)+
	  TAB_MARGIN_HEIGHT(w)*2+EHT(w)*2+LABEL_MARGIN_HEIGHT(w)*2+TAB_RAISE(w));
      if(height>greater_height)greater_height = height;
      TAB_RECT(child).x=left_x;
      TAB_RECT(child).y=0;
      TAB_RECT(child).width = width;

      if(!prev || TAB_RECT(prev).x+TAB_RECT(prev).width<=TAB_RECT(child).x){
	TAB_CLIP(child).x=TAB_RECT(child).x;
	TAB_CLIP(child).y=TAB_RECT(child).y;
	TAB_CLIP(child).width=TAB_RECT(child).width;
      } else {
	TAB_CLIP(child).x=TAB_RECT(prev).x+TAB_RECT(prev).width;
	TAB_CLIP(child).y=TAB_RECT(child).y;
	TAB_CLIP(child).width=
	  TAB_RECT(child).x+TAB_RECT(child).width-TAB_CLIP(child).x;
      }

      if(TAB_RECT(child).x+TAB_RECT(child).width >
	  TAB_RECT(DRAWING_AREA(w)).width){
	TAB_VISIBLE(child)=FALSE;
      } else {
	TAB_VISIBLE(child)=TRUE;
      }

      left_x+=(Dimension)(width+TAB_SPACING(w)-SHADOW_THICKNESS(w));
      prev=child;
    }
    for(i=0;i<NUM_CHILDREN(w);i++){
      child=CHILD(w,i);
      if(child==DRAWING_AREA(w)) continue;
      if(!XtIsManaged(child)) continue;
      TAB_RECT(child).height=greater_height;
      TAB_CLIP(child).height=TAB_RECT(child).height;
      prev=child;
    }

    TAB_RECT(DRAWING_AREA(w)).height=greater_height;
    if(greater_height<=0) TAB_RECT(DRAWING_AREA(w)).height=1;
    if(prev)
      TAB_RECT(DRAWING_AREA(w)).width=Min(TAB_RECT(DRAWING_AREA(w)).width,
	  TAB_RECT(prev).x+TAB_RECT(prev).width);

  }

  TAB_HEIGHT(w)=greater_height;

  CHILD_WIDTH(w)=WIDTH(w)-MARGIN_WIDTH(w)*2-SHADOW_THICKNESS(w)*2-
    INNER_MARGIN_WIDTH(w)*2;

  CHILD_HEIGHT(w)=HEIGHT(w)-MARGIN_HEIGHT(w)*2-SHADOW_THICKNESS(w)*2-
    INNER_MARGIN_HEIGHT(w)*2-TAB_HEIGHT(w);

  CHILD_X(w)=MARGIN_WIDTH(w)+SHADOW_THICKNESS(w)+INNER_MARGIN_WIDTH(w);
  CHILD_Y(w)=MARGIN_HEIGHT(w)+TAB_HEIGHT(w)+SHADOW_THICKNESS(w)+
    INNER_MARGIN_HEIGHT(w);
}

static void configure_children(Widget w,Widget instigator){
  int i;
  Widget child;

  for(i=0;i<NUM_CHILDREN(w);i++){
    XRectangle *rect;

    child=CHILD(w,i);
    if(!XtIsManaged(child)) continue;

    if(child==DRAWING_AREA(w)){
      rect=&TAB_RECT(child);
      if(child==instigator){
	X(child)=rect->x;
	Y(child)=rect->y;
	WIDTH(child)=rect->width;
	HEIGHT(child)=rect->height;
	BORDER(child)=0;
      } else {
	_XmConfigureObject(child,rect->x,rect->y,rect->width,rect->height,0);
      }
    } else {
      if(child==CURRENT_WIDGET(w)){
	if(child==instigator){
	  X(child)=CHILD_X(w);
	  Y(child)=CHILD_Y(w);
	  WIDTH(child)=CHILD_WIDTH(w);
	  HEIGHT(child)=CHILD_HEIGHT(w);
	  BORDER(child)=0;
	} else {
	  _XmConfigureObject(child,CHILD_X(w),CHILD_Y(w),
	      CHILD_WIDTH(w),CHILD_HEIGHT(w),0);
	}
      } else {
	if(child==instigator){
	  X(child)=X(w)-CHILD_WIDTH(w);
	  Y(child)=Y(w)-CHILD_HEIGHT(w);
	  WIDTH(child)=CHILD_WIDTH(w);
	  HEIGHT(child)=CHILD_HEIGHT(w);
	  BORDER(child)=0;
	} else {
	  _XmConfigureObject(child,X(w)-CHILD_WIDTH(w),Y(w)-CHILD_HEIGHT(w),
	      CHILD_WIDTH(w),CHILD_HEIGHT(w),0);
	}
      }
    }
  }
}

static void draw_shadows(Widget w,Region region){
  XRectangle *rect;
  XRectangle cr;
#define OPTIMIZE_SHADOW_DRAWING 0
#if OPTIMIZE_SHADOW_DRAWING
  Boolean shadow_intersect;
  XRectangle r[4];
#endif

  if(!XtIsRealized(w)) return;

  if(region) XClipBox(region,&cr);
  else { cr.x=0;cr.y=0;cr.width=WIDTH(w);cr.height=HEIGHT(w);}

  XClearArea(XtDisplay(w),XtWindow(w),cr.x,cr.y,cr.width,cr.height,FALSE);

#if OPTIMIZE_SHADOW_DRAWING
  /* left */
  r[0].x=MARGIN_WIDTH(w);
  r[0].y=MARGIN_HEIGHT(w)+TAB_HEIGHT(w);
  r[0].width=SHADOW_THICKNESS(w);
  r[0].height=HEIGHT(w)-MARGIN_HEIGHT(w)*2-TAB_HEIGHT(w);

  /* bottom */
  r[1].x=r[0].x;
  r[1].y=r[0].y+r[0].height-r[0].width;
  r[1].width=WIDTH(w)-MARGIN_WIDTH(w)*2;
  r[1].height=r[0].width;

  /* top */
  r[2].x=r[1].x;
  r[2].y=r[0].y;
  r[2].width=r[1].width;
  r[2].height=r[1].height;

  /* right */
  r[3].x=r[0].x+r[1].width-r[0].width;
  r[3].y=r[0].y;
  r[3].width=r[0].width;
  r[3].height=r[0].height;

  shadow_intersect=axyRectanglesIntersect(&r[0],&cr) | 
    axyRectanglesIntersect(&r[1],&cr) |
    axyRectanglesIntersect(&r[2],&cr) |
    axyRectanglesIntersect(&r[3],&cr);

  if(shadow_intersect) {
#endif
      XSetClipRectangles(XtDisplay(w),TOP_SHADOW_GC(w),0,0,&cr,1,Unsorted);
      XSetClipRectangles(XtDisplay(w),BOTTOM_SHADOW_GC(w),0,0,&cr,1,Unsorted);

    _XmDrawShadows(XtDisplay(w),XtWindow(w),
	  TOP_SHADOW_GC(w),BOTTOM_SHADOW_GC(w),
	  MARGIN_WIDTH(w),MARGIN_HEIGHT(w)+TAB_HEIGHT(w),
	  WIDTH(w)-MARGIN_WIDTH(w)*2,HEIGHT(w)-MARGIN_HEIGHT(w)*2-TAB_HEIGHT(w),
	  SHADOW_THICKNESS(w),XmSHADOW_OUT);
      
      XSetClipMask(XtDisplay(w),TOP_SHADOW_GC(w),None);
      XSetClipMask(XtDisplay(w),BOTTOM_SHADOW_GC(w),None);
#if OPTIMIZE_SHADOW_DRAWING
  }
#endif

  rect=&TAB_RECT(CURRENT_WIDGET(w));
  if(SHOW_TABS(w) && 
      (int)rect->x+(int)rect->width<=(int)WIDTH(w)-(int)MARGIN_WIDTH(w)*2){

#if OPTIMIZE_SHADOW_DRAWING
    if(shadow_intersect) {
#endif
      XClearArea(XtDisplay(w),XtWindow(w),
	  rect->x+SHADOW_THICKNESS(w)+MARGIN_WIDTH(w),
	  MARGIN_HEIGHT(w)+TAB_HEIGHT(w),
	  rect->width-SHADOW_THICKNESS(w)*2,SHADOW_THICKNESS(w),FALSE);
#if OPTIMIZE_SHADOW_DRAWING
    }
#endif
  }
}

static void draw_da_tabs(Widget da,Window window,XEvent *event){
  Widget w=XtParent(da);
  XExposeEvent *e=(XExposeEvent*)event;
  int i,tmp;
  XRectangle *tab_rect,*tab_clip,tab_clip_modified;
  XRectangle cr,clip;

  if(window==0) return;

  if(e==NULL){
    cr.x=0;cr.y=0;cr.width=WIDTH(da);cr.height=HEIGHT(da);
  } else {
    cr.x=e->x;cr.y=e->y;cr.width=e->width;cr.height=e->height;
  }

  XClearArea(XtDisplay((Widget)w),window,
      cr.x,cr.y,cr.width,cr.height,FALSE);

  if(SHOW_TABS(w)){
    for(i=NUM_CHILDREN(w)-1;i>=0;i--){

      if(CHILD(w,i)==da || !XtIsManaged(CHILD(w,i))) continue;
      if(CURRENT_WIDGET(w)==CHILD(w,i)) continue;
      if(!TAB_VISIBLE_I(w,i)) {
	continue;
      }

      tab_rect=&TAB_RECT_I(w,i);
      tab_clip=&TAB_CLIP_I(w,i);

      if(TAB_VISIBLE(CURRENT_WIDGET(w)) &&
	  PAGE_NUM(CURRENT_WIDGET(w))==PAGE_NUM_I(w,i)+1){
	tab_clip_modified.x=tab_clip->x;
	tab_clip_modified.y=tab_clip->y;
	tab_clip_modified.width=tmp=
	  (int)TAB_RECT(CURRENT_WIDGET(w)).x-(int)tab_clip->x;
	if(tmp<0) tab_clip_modified.width=0;
	tab_clip_modified.height=tab_clip->height;
      } else {
	tab_clip_modified.x=tab_clip->x;
	tab_clip_modified.y=tab_clip->y;
	tab_clip_modified.width=tab_clip->width;
	tab_clip_modified.height=tab_clip->height;
      }

      axyRectanglesIntersection(&cr,&tab_clip_modified,&clip);
      if(axyRectangleIsEmpty(&clip)) {
	continue;
      }

      XSetClipRectangles(XtDisplay((Widget)w),ARM_GC(w),0,0,&clip,1,Unsorted);

      XFillRectangle(XtDisplay((Widget)w),window,ARM_GC(w),
	  tab_rect->x+SHADOW_THICKNESS(w),
	  tab_rect->y+TAB_RAISE(w)+SHADOW_THICKNESS(w),
	  tab_rect->width-SHADOW_THICKNESS(w)*2,
	  tab_rect->height-TAB_RAISE(w)-SHADOW_THICKNESS(w));

      XSetClipMask(XtDisplay((Widget)w),ARM_GC(w),None);


      XSetClipRectangles(XtDisplay(w),NORMAL_GC(w),0,0,&clip,1,Unsorted);

      XmStringDraw(XtDisplay(w),window,FONT_LIST(w),
	  TAB_LABEL_I(w,i),NORMAL_GC(w),
	  tab_rect->x+SHADOW_THICKNESS(w)+TAB_MARGIN_WIDTH(w)+
	  EHT(w)+LABEL_MARGIN_WIDTH(w),
	  tab_rect->y+SHADOW_THICKNESS(w)+TAB_MARGIN_HEIGHT(w)+
	  EHT(w)+LABEL_MARGIN_HEIGHT(w)+(Dimension)TAB_RAISE(w),
	  tab_rect->width-SHADOW_THICKNESS(w)*2-TAB_MARGIN_WIDTH(w)*2-
	  EHT(w)*2-LABEL_MARGIN_WIDTH(w)*2,
	  XmALIGNMENT_CENTER,STRING_DIR(w),&clip);

      XSetClipMask(XtDisplay(w),NORMAL_GC(w),None);


      XSetClipRectangles(XtDisplay(w),TOP_SHADOW_GC(w),0,0,&clip,1,Unsorted);
      XSetClipRectangles(XtDisplay(w),BOTTOM_SHADOW_GC(w),0,0,&clip,1,Unsorted);

      _XmDrawShadows(XtDisplay(w),window,
	  TOP_SHADOW_GC(w),BOTTOM_SHADOW_GC(w),
	  tab_rect->x,tab_rect->y+(Dimension)TAB_RAISE(w),
	  tab_rect->width,tab_rect->height+SHADOW_THICKNESS(w)-
	   (Dimension)TAB_RAISE(w),
	  SHADOW_THICKNESS(w),XmSHADOW_OUT);
      
      XSetClipMask(XtDisplay(w),TOP_SHADOW_GC(w),None);
      XSetClipMask(XtDisplay(w),BOTTOM_SHADOW_GC(w),None);
    }

    tab_rect=&TAB_RECT(CURRENT_WIDGET(w));
    axyRectanglesIntersection(&cr,tab_rect,&clip);

    if(TAB_VISIBLE(CURRENT_WIDGET(w)) && !axyRectangleIsEmpty(&clip)){

      XClearArea(XtDisplay(w),window,clip.x,clip.y,
	  clip.width,clip.height,FALSE);

      XSetClipRectangles(XtDisplay(w),NORMAL_GC(w),0,0,&clip,1,Unsorted);

      XmStringDraw(XtDisplay(w),window,FONT_LIST(w),
	  TAB_LABEL(CURRENT_WIDGET(w)),NORMAL_GC(w),
	  tab_rect->x+SHADOW_THICKNESS(w)+TAB_MARGIN_WIDTH(w)+
	  EHT(w)+LABEL_MARGIN_WIDTH(w),
	  tab_rect->y+SHADOW_THICKNESS(w)+TAB_MARGIN_HEIGHT(w)+
	  EHT(w)+LABEL_MARGIN_HEIGHT(w)+
	  (Dimension)(TAB_RAISE(w)/2),
	  tab_rect->width-SHADOW_THICKNESS(w)*2-TAB_MARGIN_WIDTH(w)*2-
	  EHT(w)*2-LABEL_MARGIN_WIDTH(w)*2,
	  XmALIGNMENT_CENTER,STRING_DIR(w),&clip);

      XSetClipMask(XtDisplay(w),NORMAL_GC(w),None);


      XSetClipRectangles(XtDisplay(w),TOP_SHADOW_GC(w),0,0,&clip,1,Unsorted);
      XSetClipRectangles(XtDisplay(w),BOTTOM_SHADOW_GC(w),0,0,&clip,1,Unsorted);

      _XmDrawShadows(XtDisplay(w),window,
	  TOP_SHADOW_GC(w),BOTTOM_SHADOW_GC(w),
	  tab_rect->x, tab_rect->y,
	  tab_rect->width,tab_rect->height+SHADOW_THICKNESS(w),
	  SHADOW_THICKNESS(w),XmSHADOW_OUT);

      XSetClipMask(XtDisplay(w),TOP_SHADOW_GC(w),None);
      XSetClipMask(XtDisplay(w),BOTTOM_SHADOW_GC(w),None);
    }

    if(HIGHLIGHT_DRAWN(w)) draw_da_highlight(da);
  }

}

static void draw_da_highlight(Widget da){
  Widget w=XtParent(da);
  XRectangle *rect,*clip,clip_modified;
  GC gc=HIGHLIGHT_DRAWN(w)?HIGHLIGHT_GC(w):
      (HIGHLIGHTED_TAB(w)==CURRENT_WIDGET(w)?BACKGROUND_GC(w):ARM_GC(w));
  int tmp;

  if(HIGHLIGHT_THICKNESS(w)==0) return;
  if(!XtIsRealized(da) || !HIGHLIGHTED_TAB(w) || 
     !TAB_VISIBLE(HIGHLIGHTED_TAB(w))) return;

  rect=&TAB_RECT(HIGHLIGHTED_TAB(w));
  clip=&TAB_CLIP(HIGHLIGHTED_TAB(w));

  if(HIGHLIGHTED_TAB(w)!=CURRENT_WIDGET(w)){
    if(TAB_VISIBLE(CURRENT_WIDGET(w)) && 
	PAGE_NUM(CURRENT_WIDGET(w))==PAGE_NUM(HIGHLIGHTED_TAB(w))+1){
      clip_modified.x=clip->x;
      clip_modified.y=clip->y;
      clip_modified.width=tmp=
	(int)TAB_RECT(CURRENT_WIDGET(w)).x-(int)clip->x;
      if(tmp<0) clip_modified.width=0;
      clip_modified.height=clip->height;
    } else {
      clip_modified.x=clip->x;
      clip_modified.y=clip->y;
      clip_modified.width=clip->width;
      clip_modified.height=clip->height;
    }
    XSetClipRectangles(XtDisplay(w),gc,0,0,&clip_modified,1,Unsorted);
  }

  _XmDrawSimpleHighlight(XtDisplay(da),XtWindow(da),gc,
      rect->x+SHADOW_THICKNESS(w)+TAB_MARGIN_WIDTH(w),
      rect->y+SHADOW_THICKNESS(w)+TAB_MARGIN_HEIGHT(w)+
      (HIGHLIGHTED_TAB(w)==CURRENT_WIDGET(w)?
       (Dimension)(TAB_RAISE(w)/2):(Dimension)TAB_RAISE(w)),
      rect->width-SHADOW_THICKNESS(w)*2-TAB_MARGIN_WIDTH(w)*2,
      rect->height-SHADOW_THICKNESS(w)-TAB_MARGIN_HEIGHT(w)*2-TAB_RAISE(w),
      HIGHLIGHT_THICKNESS(w));

  XSetClipMask(XtDisplay(w),gc,None);
}

static Widget search_da_tab(Widget da, XEvent *event){
  Widget w=XtParent(da);
  int i;
  XRectangle *rect;
  Widget child;
  Position mx,my;
	
  if(!SHOW_TABS(w)) return 0;
  mx=event->xbutton.x;
  my=event->xbutton.y;

  for(i=NUM_CHILDREN(w)-1;i>=0;i--){
    child=CHILD(w,i);
    if(child==da) continue;
    if(!XtIsManaged(child)) continue;
    if(!TAB_VISIBLE(child)) continue;

    if(child==CURRENT_WIDGET(w)) {
      rect=&TAB_RECT(child);
      if(mx>=rect->x && mx<rect->x+rect->width) return child;
    } else {
      rect=&TAB_CLIP(child);
      if(mx>=rect->x && mx<rect->x+rect->width){
	if(my<=TAB_RAISE(w)) return 0;
	else return child;
      }
    }
  }
  return 0;
}

static void expose_da_cb(Widget da,XtPointer w,XtPointer call){
  XmDrawingAreaCallbackStruct *cbs=(XmDrawingAreaCallbackStruct*)call;

  if(cbs->window==0) return;

  draw_da_tabs(da,cbs->window,cbs->event);
}

static void change_tab(Widget w,Widget child){
  _XmConfigureObject(CURRENT_WIDGET(w),X(w)-CHILD_WIDTH(w),Y(w)-CHILD_HEIGHT(w),
      CHILD_WIDTH(w),CHILD_HEIGHT(w),0);
  if(HIGHLIGHTED_TAB(w) && HIGHLIGHT_DRAWN(w)){
    HIGHLIGHT_DRAWN(w)=FALSE;
    draw_da_highlight(DRAWING_AREA(w));
    HIGHLIGHT_DRAWN(w)=TRUE;
  }
  CURRENT_PAGE(w)=PAGE_NUM(child);
  CURRENT_WIDGET(w)=child;
  _XmConfigureObject(CURRENT_WIDGET(w),CHILD_X(w),CHILD_Y(w),
      CHILD_WIDTH(w),CHILD_HEIGHT(w),0);
  HIGHLIGHTED_TAB(w)=child;
  HIGHLIGHT_DRAWN(w)=TRUE;
  draw_shadows(w,NULL);
  draw_da_tabs(DRAWING_AREA(w),XtWindow(DRAWING_AREA(w)),NULL);
}



syntax highlighted by Code2HTML, v. 0.9.1