/* *********************************************************************** * This module implements a motif tabbed window widget. * The source is copied from the Free Widget Foundation * This file is divided into thse parts * o - Conversion routines for the X resource manager * o - Routines for drawing rotated text * o - A motif widget for tabbed windows * o - A driver for the above in the flavor of the xt utilities module * *********************************************************************** */ #include #include #include #include #include #include #include #include #if defined(VMS_HOST) #include #else #include #endif #include #include #include #include /* *********************************************************************** * "rotated.h" * *********************************************************************** */ /* ************************************************************************ */ /* Header file for the `xvertext 5.0' routines. Copyright (c) 1993 Alan Richardson (mppa3@uk.ac.sussex.syma) */ /* ************************************************************************ */ #ifndef _XVERTEXT_INCLUDED_ #define _XVERTEXT_INCLUDED_ #define XV_VERSION 5.0 #define XV_COPYRIGHT \ "xvertext routines Copyright (c) 1993 Alan Richardson" /* ---------------------------------------------------------------------- */ /* text alignment */ #define NONE 0 #define TLEFT 1 #define TCENTRE 2 #define TRIGHT 3 #define MLEFT 4 #define MCENTRE 5 #define MRIGHT 6 #define BLEFT 7 #define BCENTRE 8 #define BRIGHT 9 /* ---------------------------------------------------------------------- */ /* this shoulf be C++ compliant, thanks to vlp@latina.inesc.pt (Vasco Lopes Paulo) */ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { static float XRotVersion(char*, int); static void XRotSetMagnification(float); static void XRotSetBoundingBoxPad(int); static int XRotDrawString(Display*, XFontStruct*, float, Drawable, GC, int, int, char*); static int XRotDrawImageString(Display*, XFontStruct*, float, Drawable, GC, int, int, char*); static int XRotDrawAlignedString(Display*, XFontStruct*, float, Drawable, GC, int, int, char*, int); static int XRotDrawAlignedImageString(Display*, XFontStruct*, float, Drawable, GC, int, int, char*, int); static XPoint *XRotTextExtents(Display*, XFontStruct*, float, int, int, char*, int); } #else static float XRotVersion(char *str, int n); static void XRotSetMagnification(float m); static void XRotSetBoundingBoxPad(int p); static int XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str); static int XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str); static int XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align); static int XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align); static XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align); #endif /* __cplusplus */ /* ---------------------------------------------------------------------- */ #endif /* _XVERTEXT_INCLUDED_ */ /* *********************************************************************** * "strarray.h" * *********************************************************************** */ #ifndef _strarray_h_ #define _strarray_h_ /* StringArray =========== The type |StringArray| represents an array of |String|s, with the proviso that by convention the last member of a |StringArray| is always a |NULL| pointer. There is a converter that can construct a |StringArray| from a single string. cvtStringToStringArray ====================== The converter from |String| to |StringArray| makes a copy of the passed string and then replaces all occurences of the delimiter with a nul byte. The |StringArray| is filled with pointers to the parts of the string. The delimiter character is the first character in the string. newStringArray ============== The function |newStringArray| makes a copy of a |StringArray|. It allocates new space for the array itself and for the strings that it contains. freeStringArray =============== |freeStringArray| deallocates the array and all strings it contains. Note that this works for StringArrays produced with |newStringArray|, but not for those created by |cvtStringToStringArray|! */ typedef String * StringArray; static Boolean cvtStringToStringArray( #if NeedFunctionPrototypes Display *display, XrmValuePtr args, Cardinal *num_args, XrmValuePtr from, XrmValuePtr to, XtPointer *converter_data #endif ); StringArray newStringArray( #if NeedFunctionPrototypes StringArray a #endif ); void freeStringArray( #if NeedFunctionPrototypes StringArray a #endif ); #endif /* _strarray_h_ */ /* *********************************************************************** * "XmTabs.h" * *********************************************************************** */ /* Generated by wbuild from "XmTabs.w" ** (generator version Revision: 8.5 of Date: 2001/06/25 15:19:28 ) */ #ifndef _XmTabs_H_ #define _XmTabs_H_ typedef enum { XfwfUpTabs, XfwfDownTabs, XfwfLeftTabs, XfwfRightTabs, } TabsOrientation; #ifndef XtNorientation #define XtNorientation "orientation" #endif #ifndef XtCOrientation #define XtCOrientation "Orientation" #endif #ifndef XtRTabsOrientation #define XtRTabsOrientation "TabsOrientation" #endif #ifndef XtNlefttabs #define XtNlefttabs "lefttabs" #endif #ifndef XtCLefttabs #define XtCLefttabs "Lefttabs" #endif #ifndef XtRInt #define XtRInt "Int" #endif #ifndef XtNrighttabs #define XtNrighttabs "righttabs" #endif #ifndef XtCRighttabs #define XtCRighttabs "Righttabs" #endif #ifndef XtRInt #define XtRInt "Int" #endif #ifndef XtNlabels #define XtNlabels "labels" #endif #ifndef XtCLabels #define XtCLabels "Labels" #endif #ifndef XtRStringArray #define XtRStringArray "StringArray" #endif #ifndef XtNtabWidthPercentage #define XtNtabWidthPercentage "tabWidthPercentage" #endif #ifndef XtCTabWidthPercentage #define XtCTabWidthPercentage "TabWidthPercentage" #endif #ifndef XtRInt #define XtRInt "Int" #endif #ifndef XtNcornerwidth #define XtNcornerwidth "cornerwidth" #endif #ifndef XtCCornerwidth #define XtCCornerwidth "Cornerwidth" #endif #ifndef XtRCardinal #define XtRCardinal "Cardinal" #endif #ifndef XtNcornerheight #define XtNcornerheight "cornerheight" #endif #ifndef XtCCornerheight #define XtCCornerheight "Cornerheight" #endif #ifndef XtRCardinal #define XtRCardinal "Cardinal" #endif #ifndef XtNtextmargin #define XtNtextmargin "textmargin" #endif #ifndef XtCTextmargin #define XtCTextmargin "Textmargin" #endif #ifndef XtRInt #define XtRInt "Int" #endif #ifndef XtNtabcolor #define XtNtabcolor "tabcolor" #endif #ifndef XtCTabcolor #define XtCTabcolor "Tabcolor" #endif #ifndef XtRPixel #define XtRPixel "Pixel" #endif #ifndef XtNfont #define XtNfont "font" #endif #ifndef XtCFont #define XtCFont "Font" #endif #ifndef XtRFontStruct #define XtRFontStruct "FontStruct" #endif #ifndef XtNactivateCallback #define XtNactivateCallback "activateCallback" #endif #ifndef XtCActivateCallback #define XtCActivateCallback "ActivateCallback" #endif #ifndef XtRCallback #define XtRCallback "Callback" #endif typedef struct _XmTabsClassRec *XmTabsWidgetClass; typedef struct _XmTabsRec *XmTabsWidget; #endif /*_XmTabs_H_*/ /* *********************************************************************** * "XmTabsP.h" * *********************************************************************** */ /* Generated by wbuild from "XmTabs.w" ** (generator version Revision: 8.5 of Date: 2001/06/25 15:19:28 ) */ #ifndef _XmTabsP_H_ #define _XmTabsP_H_ /* raz modified 22 Jul 96 for bluestone */ #include #if ! defined(MGR_ShadowThickness) #include #endif typedef void (*border_highlight_Proc)( #if NeedFunctionPrototypes void #endif ); #define XtInherit_border_highlight ((border_highlight_Proc) _XtInherit) typedef void (*border_unhighlight_Proc)( #if NeedFunctionPrototypes void #endif ); #define XtInherit_border_unhighlight ((border_unhighlight_Proc) _XtInherit) typedef struct { /* Constraint resources */ /* Private constraint variables */ int dummy; } XmTabsConstraintPart; typedef struct _XmTabsConstraintRec { XmManagerConstraintPart xmManager; XmTabsConstraintPart xmTabs; } XmTabsConstraintRec; typedef struct { /* methods */ border_highlight_Proc border_highlight; border_unhighlight_Proc border_unhighlight; /* class variables */ } XmTabsClassPart; typedef struct _XmTabsClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ConstraintClassPart constraint_class; XmManagerClassPart xmManager_class; XmTabsClassPart xmTabs_class; } XmTabsClassRec; typedef struct { /* resources */ TabsOrientation orientation; int lefttabs; int righttabs; StringArray labels; int tabWidthPercentage; Cardinal cornerwidth; Cardinal cornerheight; int textmargin; Pixel tabcolor; XFontStruct * font; XtCallbackList activateCallback; /* private state */ GC textgc; GC topgc; GC bottomgc; GC backgc; GC fillgc; int * tabwidths; int * offsets; } XmTabsPart; typedef struct _XmTabsRec { CorePart core; CompositePart composite; ConstraintPart constraint; XmManagerPart xmManager; XmTabsPart xmTabs; } XmTabsRec; #endif /* _XmTabsP_H_ */ /* *********************************************************************** * A motif widget for tabbed windows * *********************************************************************** */ static void activate( #if NeedFunctionPrototypes Widget,XEvent*,String*,Cardinal* #endif ); static XtActionsRec actionsList[] = { {"activate", activate}, }; static char defaultTranslations[] = "\ ,: activate() \n\ "; static void _resolve_inheritance( #if NeedFunctionPrototypes WidgetClass #endif ); static void class_initialize( #if NeedFunctionPrototypes void #endif ); static void initialize( #if NeedFunctionPrototypes Widget ,Widget,ArgList ,Cardinal * #endif ); static Boolean set_values( #if NeedFunctionPrototypes Widget ,Widget ,Widget,ArgList ,Cardinal * #endif ); static void realize( #if NeedFunctionPrototypes Widget,XtValueMask *,XSetWindowAttributes * #endif ); static void resize( #if NeedFunctionPrototypes Widget #endif ); static void expose( #if NeedFunctionPrototypes Widget,XEvent *,Region #endif ); static void border_highlight( #if NeedFunctionPrototypes void #endif ); static void border_unhighlight( #if NeedFunctionPrototypes void #endif ); static void destroy( #if NeedFunctionPrototypes Widget #endif ); #define min(a, b) ((a )<(b )?(a ):(b )) #define abs(x) ((x )<0 ?-(x ):(x )) static void compute_tabsizes( #if NeedFunctionPrototypes Widget #endif ); static void comp_hor_tab_shape( #if NeedFunctionPrototypes Widget,int ,XPoint p[12],int *,int *,int * #endif ); static void comp_ver_tab_shape( #if NeedFunctionPrototypes Widget,int ,XPoint p[12],int *,int *,int * #endif ); static void draw_border( #if NeedFunctionPrototypes Widget,XPoint poly[12] #endif ); static void draw_hor_tab( #if NeedFunctionPrototypes Widget,Region ,int #endif ); static void draw_ver_tab( #if NeedFunctionPrototypes Widget,Region ,int #endif ); static void create_topgc( #if NeedFunctionPrototypes Widget #endif ); static void create_bottomgc( #if NeedFunctionPrototypes Widget #endif ); static void create_textgc( #if NeedFunctionPrototypes Widget #endif ); static void create_fillgc( #if NeedFunctionPrototypes Widget #endif ); static void create_backgc( #if NeedFunctionPrototypes Widget #endif ); static void copy_bg( #if NeedFunctionPrototypes Widget,int ,XrmValue * #endif ); static void set_shape( #if NeedFunctionPrototypes Widget #endif ); #define done(type, value) do {\ 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;\ }while (0 ) static Boolean cvtStringToTabsOrientation( #if NeedFunctionPrototypes Display *,XrmValuePtr ,Cardinal *,XrmValuePtr ,XrmValuePtr ,XtPointer * #endif ); static Boolean cvtTabsOrientationToString( #if NeedFunctionPrototypes Display *,XrmValuePtr ,Cardinal *,XrmValuePtr ,XrmValuePtr ,XtPointer * #endif ); /*ARGSUSED*/ #if NeedFunctionPrototypes static void compute_tabsizes(Widget self) #else static void compute_tabsizes(self)Widget self; #endif { int maxwd, basewidth, delta, i, n = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1; int sum, len, h, length, breadth, shad = ((XmTabsWidget)self)->xmManager.shadow_thickness; if (((XmTabsWidget)self)->xmTabs.offsets) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.offsets); if (((XmTabsWidget)self)->xmTabs.tabwidths) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.tabwidths); ((XmTabsWidget)self)->xmTabs.offsets = (XtPointer) XtMalloc(n * sizeof(*((XmTabsWidget)self)->xmTabs.offsets)); ((XmTabsWidget)self)->xmTabs.tabwidths = (XtPointer) XtMalloc(n * sizeof(*((XmTabsWidget)self)->xmTabs.tabwidths)); if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs || ((XmTabsWidget)self)->xmTabs.orientation == XfwfDownTabs) { length = ((XmTabsWidget)self)->core.width; breadth = ((XmTabsWidget)self)->core.height; } else { length = ((XmTabsWidget)self)->core.height; breadth = ((XmTabsWidget)self)->core.width; } if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage != 0) { /* Fixed width tabs */ basewidth = ((XmTabsWidget)self)->xmTabs.tabWidthPercentage * length/100; if (n > 1) delta = (length - basewidth)/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs); for (i = 0; i < n; i++) { ((XmTabsWidget)self)->xmTabs.tabwidths[i] = basewidth; ((XmTabsWidget)self)->xmTabs.offsets[i] = i * delta; } } else if (((XmTabsWidget)self)->xmTabs.labels == NULL) { /* Empty tabs */ basewidth = length/n; delta = (length - basewidth)/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs); for (i = 0; i < n; i++) { ((XmTabsWidget)self)->xmTabs.tabwidths[i] = basewidth; ((XmTabsWidget)self)->xmTabs.offsets[i] = i * delta; } } else { /* Variable width tabs */ sum = 0; h = 2 * (((XmTabsWidget)self)->xmTabs.cornerwidth + shad + ((XmTabsWidget)self)->xmTabs.textmargin); maxwd = length - n * (shad + ((XmTabsWidget)self)->xmTabs.textmargin); for (i = 0; i < n; i++) { len = strlen(((XmTabsWidget)self)->xmTabs.labels[i]); ((XmTabsWidget)self)->xmTabs.tabwidths[i] = min(maxwd, XTextWidth(((XmTabsWidget)self)->xmTabs.font,((XmTabsWidget)self)->xmTabs.labels[i],len) + h); sum += ((XmTabsWidget)self)->xmTabs.tabwidths[i]; } ((XmTabsWidget)self)->xmTabs.offsets[0] = 0; if (length >= sum) delta = (length - sum)/(n - 1); /* Between tabs */ else delta = -((sum - length + n - 2)/(n - 1)); /* Round down! */ for (i = 1; i < n; i++) ((XmTabsWidget)self)->xmTabs.offsets[i] = ((XmTabsWidget)self)->xmTabs.offsets[i-1] + ((XmTabsWidget)self)->xmTabs.tabwidths[i-1] + delta; } } /*ARGSUSED*/ #if NeedFunctionPrototypes static void comp_hor_tab_shape(Widget self,int i,XPoint p[12],int * x0,int * x1,int * midy) #else static void comp_hor_tab_shape(self,i,p,x0,x1,midy)Widget self;int i;XPoint p[12];int * x0;int * x1;int * midy; #endif { int shad = ((XmTabsWidget)self)->xmManager.shadow_thickness; int k = min(((XmTabsWidget)self)->xmTabs.cornerheight, (((XmTabsWidget)self)->core.height - shad)/2); /* * 4 o-------------o 5 * / \ * 3 o o 6 * | | * 2 o o 7 * 1 / \ 8 * 0 o--------o o--------o 9 * 11 o---------------------------------------o 10 * * 11 o---------------------------------------o 10 * 0 o--------o o--------o 9 * 1 \ / 8 * 2 o o 7 * | | * 3 o o 6 * \ / * 4 o-------------o 5 */ p[0].x = 0; p[1].x = ((XmTabsWidget)self)->xmTabs.offsets[i]; p[2].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth; p[3].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth; p[4].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth; p[5].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth; p[6].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth; p[7].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth; p[8].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i]; p[9].x = ((XmTabsWidget)self)->core.width; p[10].x = ((XmTabsWidget)self)->core.width; p[11].x = 0; if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs) { p[0].y = ((XmTabsWidget)self)->core.height - shad; p[1].y = ((XmTabsWidget)self)->core.height - shad; p[2].y = ((XmTabsWidget)self)->core.height - shad - k; p[3].y = k; p[4].y = 0; p[5].y = 0; p[6].y = k; p[7].y = ((XmTabsWidget)self)->core.height - shad - k; p[8].y = ((XmTabsWidget)self)->core.height - shad; p[9].y = ((XmTabsWidget)self)->core.height - shad; p[10].y = ((XmTabsWidget)self)->core.height; p[11].y = ((XmTabsWidget)self)->core.height; } else { p[0].y = shad; p[1].y = shad; p[2].y = shad + k; p[3].y = ((XmTabsWidget)self)->core.height - k; p[4].y = ((XmTabsWidget)self)->core.height; p[5].y = ((XmTabsWidget)self)->core.height; p[6].y = ((XmTabsWidget)self)->core.height - k; p[7].y = shad + k; p[8].y = shad; p[9].y = shad; p[10].y = 0; p[11].y = 0; } *x0 = p[4].x; *x1 = p[5].x; *midy = (p[1].y + p[4].y)/2; } /*ARGSUSED*/ #if NeedFunctionPrototypes static void comp_ver_tab_shape(Widget self,int i,XPoint p[12],int * y0,int * y1,int * midx) #else static void comp_ver_tab_shape(self,i,p,y0,y1,midx)Widget self;int i;XPoint p[12];int * y0;int * y1;int * midx; #endif { int shad = ((XmTabsWidget)self)->xmManager.shadow_thickness; int k = min(((XmTabsWidget)self)->xmTabs.cornerheight, (((XmTabsWidget)self)->core.width - shad)/2); /* * 0 o_o 11 11 o_o 0 * | | | | * 1 o | | o 1 * 3 2/ | | \2 3 * o-o | | o-o * / | | \ * 4 o | | o 4 * | | | | * 5 o | | o 5 * \ | | / * o-o | | o-o * 6 7\ | | /7 6 * 8 o | | o 8 * | | | | * 9 o_o 10 10 o_o 9 */ if (((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs) { p[0].x = ((XmTabsWidget)self)->core.width - shad; p[1].x = ((XmTabsWidget)self)->core.width - shad; p[2].x = ((XmTabsWidget)self)->core.width - shad - k; p[3].x = k; p[4].x = 0; p[5].x = 0; p[6].x = k; p[7].x = ((XmTabsWidget)self)->core.width - shad - k; p[8].x = ((XmTabsWidget)self)->core.width - shad; p[9].x = ((XmTabsWidget)self)->core.width - shad; p[10].x = ((XmTabsWidget)self)->core.width; p[11].x = ((XmTabsWidget)self)->core.width; } else { p[0].x = shad; p[1].x = shad; p[2].x = shad + k; p[3].x = ((XmTabsWidget)self)->core.width - k; p[4].x = ((XmTabsWidget)self)->core.width; p[5].x = ((XmTabsWidget)self)->core.width; p[6].x = ((XmTabsWidget)self)->core.width - k; p[7].x = shad + k; p[8].x = shad; p[9].x = shad; p[10].x = 0; p[11].x = 0; } p[0].y = 0; p[1].y = ((XmTabsWidget)self)->xmTabs.offsets[i]; p[2].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth; p[3].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth; p[4].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth; p[5].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth; p[6].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth; p[7].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth; p[8].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i]; p[9].y = ((XmTabsWidget)self)->core.height; p[10].y = ((XmTabsWidget)self)->core.height; p[11].y = 0; *y0 = p[4].y; *y1 = p[5].y; *midx = (p[1].x + p[4].x)/2; } /*ARGSUSED*/ #if NeedFunctionPrototypes static void draw_border(Widget self,XPoint poly[12]) #else static void draw_border(self,poly)Widget self;XPoint poly[12]; #endif { Display *dpy = XtDisplay(self); Window win = XtWindow(self); if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs) { XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly, 6, CoordModeOrigin); XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly + 5, 4, CoordModeOrigin); XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly + 8, 2, CoordModeOrigin); } else { XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly, 2, CoordModeOrigin); XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly + 1, 4, CoordModeOrigin); XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly + 4, 6, CoordModeOrigin); } } /*ARGSUSED*/ #if NeedFunctionPrototypes static void draw_hor_tab(Widget self,Region region,int i) #else static void draw_hor_tab(self,region,i)Widget self;Region region;int i; #endif { XPoint p[12]; Display *dpy = XtDisplay(self); Window win = XtWindow(self); Region clip; int x0, x1, midy; comp_hor_tab_shape(self, i, p, &x0, &x1, &midy); clip = XPolygonRegion(p, XtNumber(p), WindingRule); if (region) XIntersectRegion(clip, region, clip); if (XEmptyRegion(clip)) return; XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.textgc, clip); XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.topgc, clip); XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.bottomgc, clip); if (i == ((XmTabsWidget)self)->xmTabs.lefttabs) { XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.backgc, clip); XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.backgc, p, XtNumber(p), Convex, CoordModeOrigin); } else { XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.fillgc, clip); XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.fillgc, p, XtNumber(p), Convex, CoordModeOrigin); } if (((XmTabsWidget)self)->xmTabs.labels) { int w, y, x, len = strlen(((XmTabsWidget)self)->xmTabs.labels[i]); y = midy - (((XmTabsWidget)self)->xmTabs.font->ascent + ((XmTabsWidget)self)->xmTabs.font->descent)/2 + ((XmTabsWidget)self)->xmTabs.font->ascent; w = XTextWidth(((XmTabsWidget)self)->xmTabs.font, ((XmTabsWidget)self)->xmTabs.labels[i], len); if (i == ((XmTabsWidget)self)->xmTabs.lefttabs || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage <= 100/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1)) x = (x0 + x1 - w)/2; /* Centered text */ else if (i < ((XmTabsWidget)self)->xmTabs.lefttabs) x = x0 + ((XmTabsWidget)self)->xmTabs.textmargin; /* Left aligned text */ else x = x1 - ((XmTabsWidget)self)->xmTabs.textmargin - w; /* Right aligned text */ XDrawString(dpy, win, ((XmTabsWidget)self)->xmTabs.textgc, x, y, ((XmTabsWidget)self)->xmTabs.labels[i], len); } draw_border(self, p); XDestroyRegion(clip); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void draw_ver_tab(Widget self,Region region,int i) #else static void draw_ver_tab(self,region,i)Widget self;Region region;int i; #endif { Display *dpy = XtDisplay(self); Window win = XtWindow(self); XPoint p[12]; Region clip; int y0, y1, midx; comp_ver_tab_shape(self, i, p, &y0, &y1, &midx); clip = XPolygonRegion(p, XtNumber(p), WindingRule); if (region) XIntersectRegion(clip, region, clip); if (XEmptyRegion(clip)) return; XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.textgc, clip); XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.topgc, clip); XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.bottomgc, clip); if (i == ((XmTabsWidget)self)->xmTabs.lefttabs) { XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.backgc, clip); XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.backgc, p, XtNumber(p), Convex, CoordModeOrigin); } else { XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.fillgc, clip); XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.fillgc, p, XtNumber(p), Convex, CoordModeOrigin); } if (((XmTabsWidget)self)->xmTabs.labels) { int y, align; float angle = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? 90.0 : -90.0; if (i == ((XmTabsWidget)self)->xmTabs.lefttabs || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage <= 100/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1)) { y = (y0 + y1)/2; align = MCENTRE; } else if (i < ((XmTabsWidget)self)->xmTabs.lefttabs) { y = y0 + ((XmTabsWidget)self)->xmTabs.textmargin; align = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? MRIGHT : MLEFT; } else { y = y1 - ((XmTabsWidget)self)->xmTabs.textmargin; align = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? MLEFT : MRIGHT; } XRotDrawAlignedString (dpy, ((XmTabsWidget)self)->xmTabs.font, angle, win, ((XmTabsWidget)self)->xmTabs.textgc, midx, y, ((XmTabsWidget)self)->xmTabs.labels[i], align); } draw_border(self, p); XDestroyRegion(clip); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void create_topgc(Widget self) #else static void create_topgc(self)Widget self; #endif { XtGCMask mask = GCForeground | GCLineWidth; XGCValues values; if (((XmTabsWidget)self)->xmTabs.topgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.topgc); values.foreground = ((XmTabsWidget)self)->xmManager.top_shadow_color; values.line_width = 2 * ((XmTabsWidget)self)->xmManager.shadow_thickness; ((XmTabsWidget)self)->xmTabs.topgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)), mask, &values); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void create_bottomgc(Widget self) #else static void create_bottomgc(self)Widget self; #endif { XtGCMask mask = GCForeground | GCLineWidth; XGCValues values; if (((XmTabsWidget)self)->xmTabs.bottomgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.bottomgc); values.foreground = ((XmTabsWidget)self)->xmManager.bottom_shadow_color; values.line_width = 2 * ((XmTabsWidget)self)->xmManager.shadow_thickness; ((XmTabsWidget)self)->xmTabs.bottomgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)), mask, &values); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void create_textgc(Widget self) #else static void create_textgc(self)Widget self; #endif { XtGCMask mask = GCForeground | GCFont; XGCValues values; if (((XmTabsWidget)self)->xmTabs.textgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.textgc); values.foreground = ((XmTabsWidget)self)->xmManager.foreground; values.font = ((XmTabsWidget)self)->xmTabs.font->fid; ((XmTabsWidget)self)->xmTabs.textgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)), mask, &values); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void create_fillgc(Widget self) #else static void create_fillgc(self)Widget self; #endif { XtGCMask mask = GCForeground; XGCValues values; if (((XmTabsWidget)self)->xmTabs.fillgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.fillgc); values.foreground = ((XmTabsWidget)self)->xmTabs.tabcolor; ((XmTabsWidget)self)->xmTabs.fillgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)), mask, &values); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void create_backgc(Widget self) #else static void create_backgc(self)Widget self; #endif { XtGCMask mask = GCForeground; XGCValues values; if (((XmTabsWidget)self)->xmTabs.backgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.backgc); values.foreground = ((XmTabsWidget)self)->core.background_pixel; ((XmTabsWidget)self)->xmTabs.backgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)), mask, &values); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void copy_bg(Widget self,int offset,XrmValue * value) #else static void copy_bg(self,offset,value)Widget self;int offset;XrmValue * value; #endif { value->addr = (XtPointer) &((XmTabsWidget)self)->core.background_pixel; } /*ARGSUSED*/ #if NeedFunctionPrototypes static void set_shape(Widget self) #else static void set_shape(self)Widget self; #endif { int x0, x1, midy, y0, y1, midx, i; Region region, clip; XPoint poly[12]; if (! XtIsRealized(self)) return; region = XCreateRegion(); switch (((XmTabsWidget)self)->xmTabs.orientation) { case XfwfUpTabs: case XfwfDownTabs: for (i = 0; i <= ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i++) { comp_hor_tab_shape(self, i, poly, &x0, &x1, &midy); clip = XPolygonRegion(poly, XtNumber(poly), WindingRule); XUnionRegion(region, clip, region); XDestroyRegion(clip); } break; case XfwfLeftTabs: case XfwfRightTabs: for (i = 0; i <= ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i++) { comp_ver_tab_shape(self, i, poly, &y0, &y1, &midx); clip = XPolygonRegion(poly, XtNumber(poly), WindingRule); XUnionRegion(region, clip, region); XDestroyRegion(clip); } break; } XShapeCombineRegion(XtDisplay(self), XtWindow(self), ShapeBounding, 0, 0, region, ShapeSet); XDestroyRegion(region); } /*ARGSUSED*/ #if NeedFunctionPrototypes static Boolean cvtStringToTabsOrientation(Display * display,XrmValuePtr args,Cardinal * num_args,XrmValuePtr from,XrmValuePtr to,XtPointer * converter_data) #else static Boolean cvtStringToTabsOrientation(display,args,num_args,from,to,converter_data)Display * display;XrmValuePtr args;Cardinal * num_args;XrmValuePtr from;XrmValuePtr to;XtPointer * converter_data; #endif { TabsOrientation a = XfwfUpTabs; char *s = (char*) from->addr; static struct { char *name; TabsOrientation orient; } strings[] = { { "up", XfwfUpTabs }, { "uptabs", XfwfUpTabs }, { "down", XfwfDownTabs }, { "downtabs", XfwfDownTabs }, { "left", XfwfLeftTabs }, { "lefttabs", XfwfLeftTabs }, { "right", XfwfRightTabs }, { "righttabs", XfwfRightTabs }, }; int i; if (*num_args != 0) XtAppErrorMsg (XtDisplayToApplicationContext(display), "cvtStringToTabsOrientation", "wrongParameters", "XtToolkitError", "String to TabsOrientation conversion needs no arguments", (String*) NULL, (Cardinal*) NULL); for (i=0; i= XtNumber(strings) ) XtDisplayStringConversionWarning(display, s, "TabsOrientation"); done(TabsOrientation, a); } /*ARGSUSED*/ #if NeedFunctionPrototypes static Boolean cvtTabsOrientationToString(Display * display,XrmValuePtr args,Cardinal * num_args,XrmValuePtr from,XrmValuePtr to,XtPointer * converter_data) #else static Boolean cvtTabsOrientationToString(display,args,num_args,from,to,converter_data)Display * display;XrmValuePtr args;Cardinal * num_args;XrmValuePtr from;XrmValuePtr to;XtPointer * converter_data; #endif { TabsOrientation *a = (TabsOrientation*) from->addr; if (*num_args != 0) XtAppErrorMsg (XtDisplayToApplicationContext(display), "cvtTabsOrientationToString", "wrongParameters", "XtToolkitError", "TabsOrientation to String conversion needs no arguments", (String*) NULL, (Cardinal*) NULL); switch (*a) { case XfwfUpTabs: done(String, "up"); case XfwfDownTabs: done(String, "down"); case XfwfLeftTabs: done(String, "left"); case XfwfRightTabs: done(String, "right"); } XtAppErrorMsg (XtDisplayToApplicationContext(display), "cvtTabsOrientationToString", "illParameters", "XtToolkitError", "TabsOrientation to String conversion got illegal argument", (String*) NULL, (Cardinal*) NULL); return TRUE; } static XtResource resources[] = { {XtNorientation,XtCOrientation,XtRTabsOrientation,sizeof(((XmTabsRec*)NULL)->xmTabs.orientation),XtOffsetOf(XmTabsRec,xmTabs.orientation),XtRImmediate,(XtPointer)XfwfUpTabs }, {XtNlefttabs,XtCLefttabs,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.lefttabs),XtOffsetOf(XmTabsRec,xmTabs.lefttabs),XtRImmediate,(XtPointer)0 }, {XtNrighttabs,XtCRighttabs,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.righttabs),XtOffsetOf(XmTabsRec,xmTabs.righttabs),XtRImmediate,(XtPointer)0 }, {XtNlabels,XtCLabels,XtRStringArray,sizeof(((XmTabsRec*)NULL)->xmTabs.labels),XtOffsetOf(XmTabsRec,xmTabs.labels),XtRImmediate,(XtPointer)NULL }, {XtNtabWidthPercentage,XtCTabWidthPercentage,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.tabWidthPercentage),XtOffsetOf(XmTabsRec,xmTabs.tabWidthPercentage),XtRImmediate,(XtPointer)50 }, {XtNcornerwidth,XtCCornerwidth,XtRCardinal,sizeof(((XmTabsRec*)NULL)->xmTabs.cornerwidth),XtOffsetOf(XmTabsRec,xmTabs.cornerwidth),XtRImmediate,(XtPointer)3 }, {XtNcornerheight,XtCCornerheight,XtRCardinal,sizeof(((XmTabsRec*)NULL)->xmTabs.cornerheight),XtOffsetOf(XmTabsRec,xmTabs.cornerheight),XtRImmediate,(XtPointer)3 }, {XtNtextmargin,XtCTextmargin,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.textmargin),XtOffsetOf(XmTabsRec,xmTabs.textmargin),XtRImmediate,(XtPointer)3 }, {XtNtabcolor,XtCTabcolor,XtRPixel,sizeof(((XmTabsRec*)NULL)->xmTabs.tabcolor),XtOffsetOf(XmTabsRec,xmTabs.tabcolor),XtRCallProc,(XtPointer)copy_bg }, {XtNfont,XtCFont,XtRFontStruct,sizeof(((XmTabsRec*)NULL)->xmTabs.font),XtOffsetOf(XmTabsRec,xmTabs.font),XtRString,(XtPointer)XtDefaultFont }, {XtNactivateCallback,XtCActivateCallback,XtRCallback,sizeof(((XmTabsRec*)NULL)->xmTabs.activateCallback),XtOffsetOf(XmTabsRec,xmTabs.activateCallback),XtRImmediate,(XtPointer)NULL }, }; XmTabsClassRec xmTabsClassRec = { { /* core_class part */ /* superclass */ (WidgetClass) &xmManagerClassRec, /* class_name */ "XmTabs", /* widget_size */ sizeof(XmTabsRec), /* class_initialize */ class_initialize, /* class_part_initialize*/ _resolve_inheritance, /* class_inited */ FALSE, /* initialize */ initialize, /* initialize_hook */ NULL, /* realize */ realize, /* actions */ actionsList, /* num_actions */ 1, /* resources */ resources, /* num_resources */ 11, /* xrm_class */ NULLQUARK, /* compres_motion */ True , /* compress_exposure */ XtExposeCompressMultiple , /* compress_enterleave */ True , /* visible_interest */ False , /* destroy */ destroy, /* resize */ resize, /* expose */ expose, /* set_values */ set_values, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values+hook */ NULL, /* accept_focus */ XtInheritAcceptFocus, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ defaultTranslations, /* query_geometry */ XtInheritQueryGeometry, /* display_acceleator */ XtInheritDisplayAccelerator, /* extension */ NULL }, { /* composite_class part */ XtInheritGeometryManager, XtInheritChangeManaged, XtInheritInsertChild, XtInheritDeleteChild, NULL }, { /* constraint_class part */ /* constraint_resources */ NULL, /* num_constraint_resources */ 0, /* constraint_size */ sizeof(XmTabsConstraintRec), /* constraint_initialize */ NULL, /* constraint_destroy */ NULL, /* constraint_set_values */ NULL, /* constraint_extension */ NULL }, { /* XmManager class part */ #define manager_extension extension /* translations */ XtInheritTranslations , /* syn_resources */ NULL , /* num_syn_resources */ 0 , /* syn_constraint_resources */ NULL , /* num_syn_constraint_resources */ 0 , /* parent_process */ XmInheritParentProcess, /* manager_extension */ NULL , }, { /* XmTabs_class part */ border_highlight, border_unhighlight, }, }; WidgetClass xmTabsWidgetClass = (WidgetClass) &xmTabsClassRec; /*ARGSUSED*/ static void activate(Widget self, XEvent *event, String *params, Cardinal *num_params) { int x0, x1, dummy, i, x, y; XPoint poly[12]; switch (((XmTabsWidget)self)->xmTabs.orientation) { case XfwfUpTabs: case XfwfDownTabs: x = event->xbutton.x; comp_hor_tab_shape(self, ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= x && x < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) 0); return; } for (i = -1; i >= -((XmTabsWidget)self)->xmTabs.lefttabs; i--) { comp_hor_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= x && x < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i); return; } } for (i = 1; i <= ((XmTabsWidget)self)->xmTabs.righttabs; i++) { comp_hor_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= x && x < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i); return; } } break; case XfwfLeftTabs: case XfwfRightTabs: y = event->xbutton.y; comp_ver_tab_shape(self, ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= y && y < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) 0); return; } for (i = -1; i >= -((XmTabsWidget)self)->xmTabs.lefttabs; i--) { comp_ver_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= y && y < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i); return; } } for (i = 1; i <= ((XmTabsWidget)self)->xmTabs.righttabs; i++) { comp_ver_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy); if (x0 <= y && y < x1) { XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i); return; } } break; } } static void _resolve_inheritance(WidgetClass class) { XmTabsWidgetClass c = (XmTabsWidgetClass) class; XmTabsWidgetClass super; static CompositeClassExtensionRec extension_rec = { NULL, NULLQUARK, XtCompositeExtensionVersion, sizeof(CompositeClassExtensionRec), True}; CompositeClassExtensionRec *ext; ext = (XtPointer)XtMalloc(sizeof(*ext)); *ext = extension_rec; ext->next_extension = c->composite_class.extension; c->composite_class.extension = ext; if (class == xmTabsWidgetClass) return; super = (XmTabsWidgetClass)class->core_class.superclass; if (c->xmTabs_class.border_highlight == XtInherit_border_highlight) c->xmTabs_class.border_highlight = super->xmTabs_class.border_highlight; if (c->xmTabs_class.border_unhighlight == XtInherit_border_unhighlight) c->xmTabs_class.border_unhighlight = super->xmTabs_class.border_unhighlight; } /*ARGSUSED*/ #if NeedFunctionPrototypes static void class_initialize(void) #else static void class_initialize(void) #endif { XtSetTypeConverter(XtRString, "StringArray", cvtStringToStringArray, NULL, 0, XtCacheNone, NULL); XtSetTypeConverter(XtRString, "TabsOrientation", cvtStringToTabsOrientation, NULL, 0, XtCacheNone, NULL); XtSetTypeConverter("TabsOrientation", XtRString, cvtTabsOrientationToString, NULL, 0, XtCacheNone, NULL); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void initialize(Widget request,Widget self,ArgList args,Cardinal * num_args) #else static void initialize(request,self,args,num_args)Widget request;Widget self;ArgList args;Cardinal * num_args; #endif { String *h; int i; ((XmTabsWidget)self)->xmManager.traversal_on = FALSE; ((XmTabsWidget)self)->xmTabs.topgc = NULL; create_topgc(self); ((XmTabsWidget)self)->xmTabs.bottomgc = NULL; create_bottomgc(self); ((XmTabsWidget)self)->xmTabs.textgc = NULL; create_textgc(self); ((XmTabsWidget)self)->xmTabs.fillgc = NULL; create_fillgc(self); ((XmTabsWidget)self)->xmTabs.backgc = NULL; create_backgc(self); if (((XmTabsWidget)self)->xmTabs.labels) { h = (String*) XtMalloc((((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1) * sizeof(*h)); for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--) h[i] = XtNewString(((XmTabsWidget)self)->xmTabs.labels[i]); ((XmTabsWidget)self)->xmTabs.labels = h; } if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage < 0 || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage > 100) { XtAppWarning(XtWidgetToApplicationContext(self), "tabWidthPercentage out of range; reset to 50"); ((XmTabsWidget)self)->xmTabs.tabWidthPercentage = 50; } ((XmTabsWidget)self)->xmTabs.offsets = NULL; ((XmTabsWidget)self)->xmTabs.tabwidths = NULL; compute_tabsizes(self); } /*ARGSUSED*/ #if NeedFunctionPrototypes static Boolean set_values(Widget old,Widget request,Widget self,ArgList args,Cardinal * num_args) #else static Boolean set_values(old,request,self,args,num_args)Widget old;Widget request;Widget self;ArgList args;Cardinal * num_args; #endif { Bool redraw = FALSE, resize_labels = FALSE; String *h; int i; if (((XmTabsWidget)self)->core.background_pixel != ((XmTabsWidget)old)->core.background_pixel || ((XmTabsWidget)self)->core.background_pixmap != ((XmTabsWidget)old)->core.background_pixmap || ((XmTabsWidget)self)->xmManager.shadow_thickness != ((XmTabsWidget)old)->xmManager.shadow_thickness) { create_topgc(self); create_bottomgc(self); create_backgc(self); } if (((XmTabsWidget)self)->xmManager.foreground != ((XmTabsWidget)old)->xmManager.foreground || ((XmTabsWidget)self)->xmTabs.font != ((XmTabsWidget)old)->xmTabs.font) { create_textgc(self); redraw = TRUE; } if (((XmTabsWidget)self)->xmTabs.tabcolor != ((XmTabsWidget)old)->xmTabs.tabcolor) { create_fillgc(self); redraw = TRUE; } if ((((XmTabsWidget)self)->xmTabs.textmargin != ((XmTabsWidget)old)->xmTabs.textmargin && ((XmTabsWidget)self)->xmTabs.tabWidthPercentage == 0) || ((XmTabsWidget)self)->xmTabs.cornerwidth != ((XmTabsWidget)old)->xmTabs.cornerwidth || ((XmTabsWidget)self)->xmTabs.cornerheight != ((XmTabsWidget)old)->xmTabs.cornerheight) { resize_labels = TRUE; } if (((XmTabsWidget)self)->xmTabs.labels != ((XmTabsWidget)old)->xmTabs.labels) { if (((XmTabsWidget)self)->xmTabs.labels) { h = (String*) XtMalloc((((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1) * sizeof(*h)); for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--) h[i] = XtNewString(((XmTabsWidget)self)->xmTabs.labels[i]); ((XmTabsWidget)self)->xmTabs.labels = h; } if (((XmTabsWidget)old)->xmTabs.labels) { for (i = ((XmTabsWidget)old)->xmTabs.lefttabs + ((XmTabsWidget)old)->xmTabs.righttabs; i >= 0; i--) XtFree(((XmTabsWidget)old)->xmTabs.labels[i]); XtFree((XtPointer) ((XmTabsWidget)old)->xmTabs.labels); } resize_labels = TRUE; } if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage < 0 || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage > 100) { XtAppWarning(XtWidgetToApplicationContext(self), "tabWidthPercentage out of range; reset to 50"); ((XmTabsWidget)self)->xmTabs.tabWidthPercentage = 50; } if (((XmTabsWidget)old)->xmTabs.tabWidthPercentage != ((XmTabsWidget)self)->xmTabs.tabWidthPercentage) resize_labels = TRUE; if (((XmTabsWidget)self)->xmTabs.lefttabs != ((XmTabsWidget)old)->xmTabs.lefttabs || ((XmTabsWidget)self)->xmTabs.righttabs != ((XmTabsWidget)old)->xmTabs.righttabs) redraw = TRUE; if (resize_labels) { compute_tabsizes(self); redraw = TRUE; } return redraw; } /*ARGSUSED*/ #if NeedFunctionPrototypes static void realize(Widget self,XtValueMask * mask,XSetWindowAttributes * attributes) #else static void realize(self,mask,attributes)Widget self;XtValueMask * mask;XSetWindowAttributes * attributes; #endif { *mask |= CWBitGravity; attributes->bit_gravity = ForgetGravity; xmManagerClassRec.core_class.realize(self, mask, attributes); set_shape(self); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void resize(Widget self) #else static void resize(self)Widget self; #endif { if (XtIsRealized(self)) XClearArea(XtDisplay(self), XtWindow(self), 0, 0, 0, 0, True); compute_tabsizes(self); set_shape(self); if ( xmManagerClassRec.core_class.resize ) xmManagerClassRec.core_class.resize(self); } /*ARGSUSED*/ #if NeedFunctionPrototypes static void expose(Widget self,XEvent * event,Region region) #else static void expose(self,event,region)Widget self;XEvent * event;Region region; #endif { int i; if (! XtIsRealized(self)) return; switch (((XmTabsWidget)self)->xmTabs.orientation) { case XfwfUpTabs: case XfwfDownTabs: for (i = 0; i < ((XmTabsWidget)self)->xmTabs.lefttabs; i++) draw_hor_tab(self, region, i); for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i > ((XmTabsWidget)self)->xmTabs.lefttabs; i--) draw_hor_tab(self, region, i); draw_hor_tab(self, region, ((XmTabsWidget)self)->xmTabs.lefttabs); break; case XfwfLeftTabs: case XfwfRightTabs: for (i = 0; i < ((XmTabsWidget)self)->xmTabs.lefttabs; i++) draw_ver_tab(self, region, i); for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i > ((XmTabsWidget)self)->xmTabs.lefttabs; i--) draw_ver_tab(self, region, i); draw_ver_tab(self, region, ((XmTabsWidget)self)->xmTabs.lefttabs); break; } /* Focus highlight? */ } /*ARGSUSED*/ #if NeedFunctionPrototypes static void border_highlight(void) #else static void border_highlight(void) #endif { } /*ARGSUSED*/ #if NeedFunctionPrototypes static void border_unhighlight(void) #else static void border_unhighlight(void) #endif { } /*ARGSUSED*/ #if NeedFunctionPrototypes static void destroy(Widget self) #else static void destroy(self)Widget self; #endif { int i; if (((XmTabsWidget)self)->xmTabs.labels) { for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--) XtFree(((XmTabsWidget)self)->xmTabs.labels[i]); XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.labels); } if (((XmTabsWidget)self)->xmTabs.offsets) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.offsets); if (((XmTabsWidget)self)->xmTabs.tabwidths) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.tabwidths); } /* *********************************************************************** * Routines for drawing rotated text * *********************************************************************** */ /* xvertext 5.0, Copyright (c) 1993 Alan Richardson (mppa3@uk.ac.sussex.syma) * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both the * copyright notice and this permission notice appear in supporting * documentation. All work developed as a consequence of the use of * this program should duly acknowledge such use. No representations are * made about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * 8 Jun '95: [Bert Bos] added GCClipXOrigin|GCClipYOrigin|GCClipMask * when calling XCopyGC() */ /* ********************************************************************** */ /* BETTER: xvertext now does rotation at any angle!! * * BEWARE: function arguments have CHANGED since version 2.0!! */ /* ********************************************************************** */ /* ---------------------------------------------------------------------- */ /* Make sure cache size is set */ #ifndef CACHE_SIZE_LIMIT #define CACHE_SIZE_LIMIT 0 #endif /*CACHE_SIZE_LIMIT */ /* Make sure a cache method is specified */ #ifndef CACHE_XIMAGES #ifndef CACHE_BITMAPS #define CACHE_BITMAPS #endif /*CACHE_BITMAPS*/ #endif /*CACHE_XIMAGES*/ /* ---------------------------------------------------------------------- */ /* Debugging macros */ #ifdef DEBUG static int debug=1; #else static int debug=0; #endif /*DEBUG*/ #define DEBUG_PRINT1(a) if (debug) printf (a) #define DEBUG_PRINT2(a, b) if (debug) printf (a, b) #define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c) #define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d) #define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e) /* ---------------------------------------------------------------------- */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* ---------------------------------------------------------------------- */ /* A structure holding everything needed for a rotated string */ typedef struct rotated_text_item_template { Pixmap bitmap; XImage *ximage; char *text; char *font_name; Font fid; float angle; int align; float magnify; int cols_in; int rows_in; int cols_out; int rows_out; int nl; int max_width; float *corners_x; float *corners_y; long int size; int cached; struct rotated_text_item_template *next; } RotatedTextItem; RotatedTextItem *first_text_item=NULL; /* ---------------------------------------------------------------------- */ /* A structure holding current magnification and bounding box padding */ static struct style_template { float magnify; int bbx_pad; } style={ 1., 0 }; /* ---------------------------------------------------------------------- */ static char *my_strdup(char *str); static char *my_strtok(char *str1, char *str2); static float XRotVersion(char *str, int n); static void XRotSetMagnification(float m); static void XRotSetBoundingBoxPad(int p); static int XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str); static int XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str); static int XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align); static int XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align); static XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align); static XImage *MakeXImage(Display *dpy, int w, int h); static int XRotPaintAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg); static int XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg); static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, float angle, char *text, int align); static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, float angle, char *text, int align); static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item); static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item); static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage); /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Routine to mimic `strdup()' (some machines don't have it) */ /**************************************************************************/ static char *my_strdup(char *str) { char *s; if(str==NULL) return NULL; s=(char *)malloc((unsigned)(strlen(str)+1)); if(s!=NULL) strcpy(s, str); return s; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Routine to replace `strtok' : this one returns a zero length string if */ /* it encounters two consecutive delimiters */ /**************************************************************************/ static char *my_strtok(char *str1, char *str2) { char *ret; int i, j, stop; static int start, len; static char *stext; if(str2==NULL) return NULL; /* initialise if str1 not NULL */ if(str1!=NULL) { start=0; stext=str1; len=strlen(str1); } /* run out of tokens ? */ if(start>=len) return NULL; /* loop through characters */ for(i=start; i0.) style.magnify=m; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Set the padding used when calculating bounding boxes */ /**************************************************************************/ static void XRotSetBoundingBoxPad(int p) { if(p>=0) style.bbx_pad=p; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Create an XImage structure and allocate memory for it */ /**************************************************************************/ static XImage *MakeXImage(Display *dpy, int w, int h) { XImage *I; char *data; /* reserve memory for image */ data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1); if(data==NULL) return NULL; /* create the XImage */ I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap, 0, data, w, h, 8, 0); if(I==NULL) return NULL; I->byte_order=I->bitmap_bit_order=MSBFirst; return I; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* A front end to XRotPaintAlignedString: */ /* -no alignment, no background */ /**************************************************************************/ static int XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str) { return (XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, str, NONE, 0)); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* A front end to XRotPaintAlignedString: */ /* -no alignment, paints background */ /**************************************************************************/ static int XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str) { return(XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, str, NONE, 1)); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* A front end to XRotPaintAlignedString: */ /* -does alignment, no background */ /**************************************************************************/ static int XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align) { return(XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, text, align, 0)); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* A front end to XRotPaintAlignedString: */ /* -does alignment, paints background */ /**************************************************************************/ static int XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align) { return(XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, text, align, 1)); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Aligns and paints a rotated string */ /**************************************************************************/ static int XRotPaintAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg) { int i; GC my_gc; int xp, yp; float hot_x, hot_y; float hot_xp, hot_yp; float sin_angle, cos_angle; RotatedTextItem *item; Pixmap bitmap_to_paint; /* return early for NULL/empty strings */ if(text==NULL) return 0; if(strlen(text)==0) return 0; /* manipulate angle to 0<=angle<360 degrees */ while(angle<0) angle+=360; while(angle>=360) angle-=360; angle*=M_PI/180; /* horizontal text made easy */ if(angle==0. && style.magnify==1.) return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y, text, align, bg)); /* get a rotated bitmap */ item=XRotRetrieveFromCache(dpy, font, angle, text, align); if(item==NULL) return NULL; /* this gc has similar properties to the user's gc */ my_gc=XCreateGC(dpy, drawable, NULL, 0); XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask |GCClipXOrigin|GCClipYOrigin|GCClipMask, my_gc); /* alignment : which point (hot_x, hot_y) relative to bitmap centre coincides with user's specified point? */ /* y position */ if(align==TLEFT || align==TCENTRE || align==TRIGHT) hot_y=(float)item->rows_in/2*style.magnify; else if(align==MLEFT || align==MCENTRE || align==MRIGHT) hot_y=0; else if(align==BLEFT || align==BCENTRE || align==BRIGHT) hot_y = -(float)item->rows_in/2*style.magnify; else hot_y = -((float)item->rows_in/2-(float)font->descent)*style.magnify; /* x position */ if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE) hot_x = -(float)item->max_width/2*style.magnify; else if(align==TCENTRE || align==MCENTRE || align==BCENTRE) hot_x=0; else hot_x=(float)item->max_width/2*style.magnify; /* pre-calculate sin and cos */ sin_angle=sin(angle); cos_angle=cos(angle); /* rotate hot_x and hot_y around bitmap centre */ hot_xp= hot_x*cos_angle - hot_y*sin_angle; hot_yp= hot_x*sin_angle + hot_y*cos_angle; /* text background will be drawn using XFillPolygon */ if(bg) { GC depth_one_gc; XPoint *xpoints; Pixmap empty_stipple; /* reserve space for XPoints */ xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint))); if(!xpoints) return 1; /* rotate corner positions */ for(i=0; i<4*item->nl; i++) { xpoints[i].x=(float)x + ( (item->corners_x[i]-hot_x)*cos_angle + (item->corners_y[i]+hot_y)*sin_angle); xpoints[i].y=(float)y + (-(item->corners_x[i]-hot_x)*sin_angle + (item->corners_y[i]+hot_y)*cos_angle); } /* we want to swap foreground and background colors here; XGetGCValues() is only available in R4+ */ empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1); depth_one_gc=XCreateGC(dpy, empty_stipple, NULL, 0); XSetForeground(dpy, depth_one_gc, 0); XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2); XSetStipple(dpy, my_gc, empty_stipple); XSetFillStyle(dpy, my_gc, FillOpaqueStippled); XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex, CoordModeOrigin); /* free our resources */ free((char *)xpoints); XFreeGC(dpy, depth_one_gc); XFreePixmap(dpy, empty_stipple); } /* where should top left corner of bitmap go ? */ xp=(float)x-((float)item->cols_out/2 +hot_xp); yp=(float)y-((float)item->rows_out/2 -hot_yp); /* by default we draw the rotated bitmap, solid */ bitmap_to_paint=item->bitmap; /* handle user stippling */ #ifndef X11R3 { GC depth_one_gc; XGCValues values; Pixmap new_bitmap, inverse; /* try and get some GC properties */ if(XGetGCValues(dpy, gc, GCStipple|GCFillStyle|GCForeground|GCBackground| GCTileStipXOrigin|GCTileStipYOrigin, &values)) { /* only do this if stippling requested */ if((values.fill_style==FillStippled || values.fill_style==FillOpaqueStippled) && !bg) { /* opaque stipple: draw rotated text in background colour */ if(values.fill_style==FillOpaqueStippled) { XSetForeground(dpy, my_gc, values.background); XSetFillStyle(dpy, my_gc, FillStippled); XSetStipple(dpy, my_gc, item->bitmap); XSetTSOrigin(dpy, my_gc, xp, yp); XFillRectangle(dpy, drawable, my_gc, xp, yp, item->cols_out, item->rows_out); XSetForeground(dpy, my_gc, values.foreground); } /* this will merge the rotated text and the user's stipple */ new_bitmap=XCreatePixmap(dpy, drawable, item->cols_out, item->rows_out, 1); /* create a GC */ depth_one_gc=XCreateGC(dpy, new_bitmap, NULL, 0); XSetForeground(dpy, depth_one_gc, 1); XSetBackground(dpy, depth_one_gc, 0); /* set the relative stipple origin */ XSetTSOrigin(dpy, depth_one_gc, values.ts_x_origin-xp, values.ts_y_origin-yp); /* fill the whole bitmap with the user's stipple */ XSetStipple(dpy, depth_one_gc, values.stipple); XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled); XFillRectangle(dpy, new_bitmap, depth_one_gc, 0, 0, item->cols_out, item->rows_out); /* set stipple origin back to normal */ XSetTSOrigin(dpy, depth_one_gc, 0, 0); /* this will contain an inverse copy of the rotated text */ inverse=XCreatePixmap(dpy, drawable, item->cols_out, item->rows_out, 1); /* invert text */ XSetFillStyle(dpy, depth_one_gc, FillSolid); XSetFunction(dpy, depth_one_gc, GXcopyInverted); XCopyArea(dpy, item->bitmap, inverse, depth_one_gc, 0, 0, item->cols_out, item->rows_out, 0, 0); /* now delete user's stipple everywhere EXCEPT on text */ XSetForeground(dpy, depth_one_gc, 0); XSetBackground(dpy, depth_one_gc, 1); XSetStipple(dpy, depth_one_gc, inverse); XSetFillStyle(dpy, depth_one_gc, FillStippled); XSetFunction(dpy, depth_one_gc, GXcopy); XFillRectangle(dpy, new_bitmap, depth_one_gc, 0, 0, item->cols_out, item->rows_out); /* free resources */ XFreePixmap(dpy, inverse); XFreeGC(dpy, depth_one_gc); /* this is the new bitmap */ bitmap_to_paint=new_bitmap; } } } #endif /*X11R3*/ /* paint text using stipple technique */ XSetFillStyle(dpy, my_gc, FillStippled); XSetStipple(dpy, my_gc, bitmap_to_paint); XSetTSOrigin(dpy, my_gc, xp, yp); XFillRectangle(dpy, drawable, my_gc, xp, yp, item->cols_out, item->rows_out); /* free our resources */ XFreeGC(dpy, my_gc); /* stippled bitmap no longer needed */ if(bitmap_to_paint!=item->bitmap) XFreePixmap(dpy, bitmap_to_paint); #ifdef CACHE_XIMAGES XFreePixmap(dpy, item->bitmap); #endif /*CACHE_XIMAGES*/ /* if item isn't cached, destroy it completely */ if(!item->cached) XRotFreeTextItem(dpy,item); /* we got to the end OK! */ return 0; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Draw a horizontal string in a quick fashion */ /**************************************************************************/ static int XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg) { GC my_gc; int nl=1, i; int height; int xp, yp; char *str1, *str2, *str3; char *str2_a="\0", *str2_b="\n\0"; int dir, asc, desc; XCharStruct overall; DEBUG_PRINT1("**\nHorizontal text.\n"); /* this gc has similar properties to the user's gc (including stipple) */ my_gc=XCreateGC(dpy, drawable, NULL, 0); XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle| GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask| GCClipXOrigin|GCClipYOrigin|GCClipMask, my_gc); XSetFont(dpy, my_gc, font->fid); /* count number of sections in string */ if(align!=NONE) for(i=0; iascent+font->descent; /* y position */ if(align==TLEFT || align==TCENTRE || align==TRIGHT) yp=y+font->ascent; else if(align==MLEFT || align==MCENTRE || align==MRIGHT) yp=y-nl*height/2+font->ascent; else if(align==BLEFT || align==BCENTRE || align==BRIGHT) yp=y-nl*height+font->ascent; else yp=y; str1=my_strdup(text); if(str1==NULL) return 1; str3=my_strtok(str1, str2); /* loop through each section in the string */ do { XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc, &overall); /* where to draw section in x ? */ if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE) xp=x; else if(align==TCENTRE || align==MCENTRE || align==BCENTRE) xp=x-overall.rbearing/2; else xp=x-overall.rbearing; /* draw string onto bitmap */ if(!bg) XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3)); else XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3)); /* move to next line */ yp+=height; str3=my_strtok((char *)NULL, str2); } while(str3!=NULL); free(str1); XFreeGC(dpy, my_gc); return 0; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Query cache for a match with this font/text/angle/alignment */ /* request, otherwise arrange for its creation */ /**************************************************************************/ static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, float angle, char *text, int align) { Font fid; char *font_name=NULL; unsigned long name_value; RotatedTextItem *item=NULL; RotatedTextItem *i1=first_text_item; /* get font name, if it exists */ if(XGetFontProperty(font, XA_FONT, &name_value)) { DEBUG_PRINT1("got font name OK\n"); font_name=XGetAtomName(dpy, name_value); fid=0; } #ifdef CACHE_FID /* otherwise rely (unreliably?) on font ID */ else { DEBUG_PRINT1("can't get fontname, caching FID\n"); font_name=NULL; fid=font->fid; } #else /* not allowed to cache font ID's */ else { DEBUG_PRINT1("can't get fontname, can't cache\n"); font_name=NULL; fid=0; } #endif /*CACHE_FID*/ /* look for a match in cache */ /* matching formula: identical text; identical fontname (if defined, font ID's if not); angles close enough (<0.00001 here, could be smaller); HORIZONTAL alignment matches, OR it's a one line string; magnifications the same */ while(i1 && !item) { /* match everything EXCEPT fontname/ID */ if(strcmp(text, i1->text)==0 && fabs(angle-i1->angle)<0.00001 && style.magnify==i1->magnify && (i1->nl==1 || ((align==0)?9:(align-1))%3== ((i1->align==0)?9:(i1->align-1))%3)) { /* now match fontname/ID */ if(font_name!=NULL && i1->font_name!=NULL) { if(strcmp(font_name, i1->font_name)==0) { item=i1; DEBUG_PRINT1("Matched against font names\n"); } else i1=i1->next; } #ifdef CACHE_FID else if(font_name==NULL && i1->font_name==NULL) { if(fid==i1->fid) { item=i1; DEBUG_PRINT1("Matched against FID's\n"); } else i1=i1->next; } #endif /*CACHE_FID*/ else i1=i1->next; } else i1=i1->next; } if(item) DEBUG_PRINT1("**\nFound target in cache.\n"); if(!item) DEBUG_PRINT1("**\nNo match in cache.\n"); /* no match */ if(!item) { /* create new item */ item=XRotCreateTextItem(dpy, font, angle, text, align); if(!item) return NULL; /* record what it shows */ item->text=my_strdup(text); /* fontname or ID */ if(font_name!=NULL) { item->font_name=my_strdup(font_name); item->fid=0; } else { item->font_name=NULL; item->fid=fid; } item->angle=angle; item->align=align; item->magnify=style.magnify; /* cache it */ XRotAddToLinkedList(dpy, item); } if(font_name) XFree(font_name); /* if XImage is cached, need to recreate the bitmap */ #ifdef CACHE_XIMAGES { GC depth_one_gc; /* create bitmap to hold rotated text */ item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy), item->cols_out, item->rows_out, 1); /* depth one gc */ depth_one_gc=XCreateGC(dpy, item->bitmap, NULL, 0); XSetBackground(dpy, depth_one_gc, 0); XSetForeground(dpy, depth_one_gc, 1); /* make the text bitmap from XImage */ XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0, item->cols_out, item->rows_out); XFreeGC(dpy, depth_one_gc); } #endif /*CACHE_XIMAGES*/ return item; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Create a rotated text item */ /**************************************************************************/ static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, float angle, char *text, int align) { RotatedTextItem *item=NULL; Pixmap canvas; GC font_gc; XImage *I_in; register int i, j; char *str1, *str2, *str3; char *str2_a="\0", *str2_b="\n\0"; int height; int byte_w_in, byte_w_out; int xp, yp; float sin_angle, cos_angle; int it, jt; float di, dj; int ic=0; float xl, xr, xinc; int byte_out; int dir, asc, desc; XCharStruct overall; int old_cols_in=0, old_rows_in=0; /* allocate memory */ item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem)); if(!item) return NULL; /* count number of sections in string */ item->nl=1; if(align!=NONE) for(i=0; inl++; /* ignore newline characters if not doing alignment */ if(align==NONE) str2=str2_a; else str2=str2_b; /* find width of longest section */ str1=my_strdup(text); if(str1==NULL) return NULL; str3=my_strtok(str1, str2); XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc, &overall); item->max_width=overall.rbearing; /* loop through each section */ do { str3=my_strtok((char *)NULL, str2); if(str3!=NULL) { XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc, &overall); if(overall.rbearing>item->max_width) item->max_width=overall.rbearing; } } while(str3!=NULL); free(str1); /* overall font height */ height=font->ascent+font->descent; /* dimensions horizontal text will have */ item->cols_in=item->max_width; item->rows_in=item->nl*height; /* bitmap for drawing on */ canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy), item->cols_in, item->rows_in, 1); /* create a GC for the bitmap */ font_gc=XCreateGC(dpy, canvas, NULL, 0); XSetBackground(dpy, font_gc, 0); XSetFont(dpy, font_gc, font->fid); /* make sure the bitmap is blank */ XSetForeground(dpy, font_gc, 0); XFillRectangle(dpy, canvas, font_gc, 0, 0, item->cols_in+1, item->rows_in+1); XSetForeground(dpy, font_gc, 1); /* pre-calculate sin and cos */ sin_angle=sin(angle); cos_angle=cos(angle); /* text background will be drawn using XFillPolygon */ item->corners_x= (float *)malloc((unsigned)(4*item->nl*sizeof(float))); if(!item->corners_x) return NULL; item->corners_y= (float *)malloc((unsigned)(4*item->nl*sizeof(float))); if(!item->corners_y) return NULL; /* draw text horizontally */ /* start at top of bitmap */ yp=font->ascent; str1=my_strdup(text); if(str1==NULL) return NULL; str3=my_strtok(str1, str2); /* loop through each section in the string */ do { XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc, &overall); /* where to draw section in x ? */ if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE) xp=0; else if(align==TCENTRE || align==MCENTRE || align==BCENTRE) xp=(item->max_width-overall.rbearing)/2; else xp=item->max_width-overall.rbearing; /* draw string onto bitmap */ XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3)); /* keep a note of corner positions of this string */ item->corners_x[ic]=((float)xp-(float)item->cols_in/2)*style.magnify; item->corners_y[ic]=((float)(yp-font->ascent)-(float)item->rows_in/2) *style.magnify; item->corners_x[ic+1]=item->corners_x[ic]; item->corners_y[ic+1]=item->corners_y[ic]+(float)height*style.magnify; item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+ (float)overall.rbearing*style.magnify; item->corners_y[item->nl*4-1-ic]=item->corners_y[ic]; item->corners_x[item->nl*4-2-ic]= item->corners_x[item->nl*4-1-ic]; item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1]; ic+=2; /* move to next line */ yp+=height; str3=my_strtok((char *)NULL, str2); } while(str3!=NULL); free(str1); /* create image to hold horizontal text */ I_in=MakeXImage(dpy, item->cols_in, item->rows_in); if(I_in==NULL) return NULL; /* extract horizontal text */ XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in, 1, XYPixmap, I_in, 0, 0); I_in->format=XYBitmap; /* magnify horizontal text */ if(style.magnify!=1.) { I_in=XRotMagnifyImage(dpy, I_in); old_cols_in=item->cols_in; old_rows_in=item->rows_in; item->cols_in=(float)item->cols_in*style.magnify; item->rows_in=(float)item->rows_in*style.magnify; } /* how big will rotated text be ? */ item->cols_out=fabs((float)item->rows_in*sin_angle) + fabs((float)item->cols_in*cos_angle) +0.99999 +2; item->rows_out=fabs((float)item->rows_in*cos_angle) + fabs((float)item->cols_in*sin_angle) +0.99999 +2; if(item->cols_out%2==0) item->cols_out++; if(item->rows_out%2==0) item->rows_out++; /* create image to hold rotated text */ item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out); if(item->ximage==NULL) return NULL; byte_w_in=(item->cols_in-1)/8+1; byte_w_out=(item->cols_out-1)/8+1; /* we try to make this bit as fast as possible - which is why it looks a bit over-the-top */ /* vertical distance from centre */ dj=0.5-(float)item->rows_out/2; /* where abouts does text actually lie in rotated image? */ if(angle==0 || angle==M_PI/2 || angle==M_PI || angle==3*M_PI/2) { xl=0; xr=(float)item->cols_out; xinc=0; } else if(anglecols_out/2+ (dj-(float)item->rows_in/(2*cos_angle))/ tan(angle)-2; xr=(float)item->cols_out/2+ (dj+(float)item->rows_in/(2*cos_angle))/ tan(angle)+2; xinc=1./tan(angle); } else { xl=(float)item->cols_out/2+ (dj+(float)item->rows_in/(2*cos_angle))/ tan(angle)-2; xr=(float)item->cols_out/2+ (dj-(float)item->rows_in/(2*cos_angle))/ tan(angle)+2; xinc=1./tan(angle); } /* loop through all relevent bits in rotated image */ for(j=0; jrows_out; j++) { /* no point re-calculating these every pass */ di=(float)((xl<0)?0:(int)xl)+0.5-(float)item->cols_out/2; byte_out=(item->rows_out-j-1)*byte_w_out; /* loop through meaningful columns */ for(i=((xl<0)?0:(int)xl); i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) { /* rotate coordinates */ it=(float)item->cols_in/2 + ( di*cos_angle + dj*sin_angle); jt=(float)item->rows_in/2 - (-di*sin_angle + dj*cos_angle); /* set pixel if required */ if(it>=0 && itcols_in && jt>=0 && jtrows_in) if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0) item->ximage->data[byte_out+i/8]|=128>>i%8; di+=1; } dj+=1; xl+=xinc; xr+=xinc; } XDestroyImage(I_in); if(style.magnify!=1.) { item->cols_in=old_cols_in; item->rows_in=old_rows_in; } #ifdef CACHE_BITMAPS /* create a bitmap to hold rotated text */ item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy), item->cols_out, item->rows_out, 1); /* make the text bitmap from XImage */ XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0, item->cols_out, item->rows_out); XDestroyImage(item->ximage); #endif /*CACHE_BITMAPS*/ XFreeGC(dpy, font_gc); XFreePixmap(dpy, canvas); return item; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Adds a text item to the end of the cache, removing as many items */ /* from the front as required to keep cache size below limit */ /**************************************************************************/ static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item) { static long int current_size=0; static RotatedTextItem *last=NULL; RotatedTextItem *i1=first_text_item, *i2=NULL; #ifdef CACHE_BITMAPS /* I don't know how much memory a pixmap takes in the server - probably this + a bit more we can't account for */ item->size=((item->cols_out-1)/8+1)*item->rows_out; #else /* this is pretty much the size of a RotatedTextItem */ item->size=((item->cols_out-1)/8+1)*item->rows_out + sizeof(XImage) + strlen(item->text) + item->nl*8*sizeof(float) + sizeof(RotatedTextItem); if(item->font_name!=NULL) item->size+=strlen(item->font_name); else item->size+=sizeof(Font); #endif /*CACHE_BITMAPS */ #ifdef DEBUG /* count number of items in cache, for debugging */ { int i=0; while(i1) { i++; i1=i1->next; } DEBUG_PRINT2("Cache has %d items.\n", i); i1=first_text_item; } #endif DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%ld\n", current_size, item->size, CACHE_SIZE_LIMIT*1024); /* if this item is bigger than whole cache, forget it */ if(item->size>CACHE_SIZE_LIMIT*1024) { DEBUG_PRINT1("Too big to cache\n\n"); item->cached=0; return; } /* remove elements from cache as needed */ while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) { DEBUG_PRINT2("Removed %d bytes\n", i1->size); if(i1->font_name!=NULL) DEBUG_PRINT5(" (`%s'\n %s\n angle=%f align=%d)\n", i1->text, i1->font_name, i1->angle, i1->align); #ifdef CACHE_FID if(i1->font_name==NULL) DEBUG_PRINT5(" (`%s'\n FID=%ld\n angle=%f align=%d)\n", i1->text, i1->fid, i1->angle, i1->align); #endif /*CACHE_FID*/ current_size-=i1->size; i2=i1->next; /* free resources used by the unlucky item */ XRotFreeTextItem(dpy, i1); /* remove it from linked list */ first_text_item=i2; i1=i2; } /* add new item to end of linked list */ if(first_text_item==NULL) { item->next=NULL; first_text_item=item; last=item; } else { item->next=NULL; last->next=item; last=item; } /* new cache size */ current_size+=item->size; item->cached=1; DEBUG_PRINT1("Added item to cache.\n"); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Free the resources used by a text item */ /**************************************************************************/ static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item) { free(item->text); if(item->font_name!=NULL) free(item->font_name); free((char *)item->corners_x); free((char *)item->corners_y); #ifdef CACHE_BITMAPS XFreePixmap(dpy, item->bitmap); #else XDestroyImage(item->ximage); #endif /* CACHE_BITMAPS */ free((char *)item); } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Magnify an XImage using bilinear interpolation */ /**************************************************************************/ static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage) { int i, j; float x, y; float u,t; XImage *I_out; int cols_in, rows_in; int cols_out, rows_out; register int i2, j2; float z1, z2, z3, z4; int byte_width_in, byte_width_out; float mag_inv; /* size of input image */ cols_in=ximage->width; rows_in=ximage->height; /* size of final image */ cols_out=(float)cols_in*style.magnify; rows_out=(float)rows_in*style.magnify; /* this will hold final image */ I_out=MakeXImage(dpy, cols_out, rows_out); if(I_out==NULL) return NULL; /* width in bytes of input, output images */ byte_width_in=(cols_in-1)/8+1; byte_width_out=(cols_out-1)/8+1; /* for speed */ mag_inv=1./style.magnify; y=0.; /* loop over magnified image */ for(j2=0; j2data[j*byte_width_in+i/8] & 128>>(i%8))>0; z2=z1; z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0; z4=z3; } /* top edge */ else if(i!=cols_in-1 && j==rows_in-1) { t=x-(float)i; u=0; z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0; z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0; z3=z2; z4=z1; } /* top right corner */ else if(i==cols_in-1 && j==rows_in-1) { u=0; t=0; z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0; z2=z1; z3=z1; z4=z1; } /* somewhere `safe' */ else { t=x-(float)i; u=y-(float)j; z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0; z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0; z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0; z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0; } /* if interpolated value is greater than 0.5, set bit */ if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5) I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8; x+=mag_inv; } y+=mag_inv; } /* destroy original */ XDestroyImage(ximage); /* return big image */ return I_out; } /* ---------------------------------------------------------------------- */ /**************************************************************************/ /* Calculate the bounding box some text will have when painted */ /**************************************************************************/ static XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align) { register int i; char *str1, *str2, *str3; char *str2_a="\0", *str2_b="\n\0"; int height; float sin_angle, cos_angle; int nl, max_width; int cols_in, rows_in; float hot_x, hot_y; XPoint *xp_in, *xp_out; int dir, asc, desc; XCharStruct overall; /* manipulate angle to 0<=angle<360 degrees */ while(angle<0) angle+=360; while(angle>360) angle-=360; angle*=M_PI/180; /* count number of sections in string */ nl=1; if(align!=NONE) for(i=0; imax_width) max_width=overall.rbearing; } } while(str3!=NULL); free(str1); /* overall font height */ height=font->ascent+font->descent; /* dimensions horizontal text will have */ cols_in=max_width; rows_in=nl*height; /* pre-calculate sin and cos */ sin_angle=sin(angle); cos_angle=cos(angle); /* y position */ if(align==TLEFT || align==TCENTRE || align==TRIGHT) hot_y=(float)rows_in/2*style.magnify; else if(align==MLEFT || align==MCENTRE || align==MRIGHT) hot_y=0; else if(align==BLEFT || align==BCENTRE || align==BRIGHT) hot_y = -(float)rows_in/2*style.magnify; else hot_y = -((float)rows_in/2-(float)font->descent)*style.magnify; /* x position */ if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE) hot_x = -(float)max_width/2*style.magnify; else if(align==TCENTRE || align==MCENTRE || align==BCENTRE) hot_x=0; else hot_x=(float)max_width/2*style.magnify; /* reserve space for XPoints */ xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint))); if(!xp_in) return NULL; xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint))); if(!xp_out) return NULL; /* bounding box when horizontal, relative to bitmap centre */ xp_in[0].x = -(float)cols_in*style.magnify/2-style.bbx_pad; xp_in[0].y= (float)rows_in*style.magnify/2+style.bbx_pad; xp_in[1].x= (float)cols_in*style.magnify/2+style.bbx_pad; xp_in[1].y= (float)rows_in*style.magnify/2+style.bbx_pad; xp_in[2].x= (float)cols_in*style.magnify/2+style.bbx_pad; xp_in[2].y = -(float)rows_in*style.magnify/2-style.bbx_pad; xp_in[3].x = -(float)cols_in*style.magnify/2-style.bbx_pad; xp_in[3].y = -(float)rows_in*style.magnify/2-style.bbx_pad; xp_in[4].x=xp_in[0].x; xp_in[4].y=xp_in[0].y; /* rotate and translate bounding box */ for(i=0; i<5; i++) { xp_out[i].x=(float)x + ( ((float)xp_in[i].x-hot_x)*cos_angle + ((float)xp_in[i].y+hot_y)*sin_angle); xp_out[i].y=(float)y + (-((float)xp_in[i].x-hot_x)*sin_angle + ((float)xp_in[i].y+hot_y)*cos_angle); } free((char *)xp_in); return xp_out; } /* *********************************************************************** * Conversion routines for the X resource manager * *********************************************************************** */ #if defined(__STDC__) static Boolean strtocard( Display *dsp, XrmValue *args, Cardinal *num_args, XrmValue *from, XrmValue *to, XtPointer *unused ) #else static Boolean strtocard( dsp, args, num_args, from, to, unused ) Display *dsp; XrmValue *args; Cardinal *num_args; XrmValue *from; XrmValue *to; XtPointer *unused; #endif { static Cardinal temp; if ( to->addr == NULL ) { to->addr = (XtPointer) &temp; to->size = sizeof(Cardinal); } *((Cardinal *) to->addr) = atoi( from->addr ); return True; } #define done_bert(type, value) \ do {\ 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;\ } while (0) static Boolean cvtStringToStringArray(Display *display, XrmValuePtr args, Cardinal *num_args, XrmValuePtr from, XrmValuePtr to, XtPointer *converter_data) { String t, s; StringArray a = NULL; Cardinal i; char delim; if (*num_args != 0) XtAppErrorMsg(XtDisplayToApplicationContext(display), "cvtStringToStringArray", "wrongParameters", "XtToolkitError", "String to StringArray conversion needs no arguments", (String*) NULL, (Cardinal*) NULL); delim = ((String) from->addr)[0]; s = XtNewString((String) from->addr + 1); i = 0; while (s && *s) { t = strchr(s, delim); if (t) *t = '\0'; a = (StringArray) XtRealloc((String) a, (i + 1) * sizeof(*a)); a[i] = s; i++; s = t ? t + 1 : NULL; } a = (StringArray) XtRealloc((String) a, (i + 1) * sizeof(*a)); a[i] = NULL; done_bert(StringArray, a); } /* *********************************************************************** * A driver for the above in the flavor of the xt utilities module * *********************************************************************** */ #define TABHT 25 typedef struct tab_data { Widget form; int cur, num_tabs; void (*activate_func)(); } *TabData; #if defined(__STDC__) static void handle_click( Widget w, TabData td, XtPointer call_data ) #else static void handle_click(w, td, call_data) Widget w; TabData td; XtPointer call_data; #endif { int tab = (int) call_data; /* note that the tab is relative to the current tab. * if tab is 0, the user clicked on the current one. * there is nothing to do */ if (tab == 0) return; td->cur += tab; /* Change tabs. We must manually inform the UI which tab is current */ XtVaSetValues( w, XtNlefttabs, td->cur, XtNrighttabs, td->num_tabs - td->cur - 1, NULL ); (*td->activate_func)( td->form, td->cur ); } /* * PUBLIC: Widget __vi_CreateTabbedFolder * PUBLIC: __P((String, Widget, String, int, void (*)(Widget, int))); */ #if defined(__STDC__) Widget __vi_CreateTabbedFolder( String name, Widget parent, String tab_labels, int num_tabs, void (*activate_func)() ) #else Widget __vi_CreateTabbedFolder( name, parent, tab_labels, num_tabs, activate_func ) String name; String tab_labels; Widget parent; int num_tabs; void (*activate_func)(); #endif { Widget tabs; TabData td = (TabData) malloc( sizeof(struct tab_data) ); int i; XtAppSetTypeConverter( XtDisplayToApplicationContext(XtDisplay(parent)), XtRString, XtRCardinal, strtocard, NULL, 0, XtCacheNone, NULL ); /* init our internal structure */ td->cur = 0; td->num_tabs = num_tabs; td->activate_func = activate_func; /* tabs go on the top */ tabs = XtVaCreateManagedWidget( "tabs", xmTabsWidgetClass, parent, XtNlefttabs, 0, XtNrighttabs, num_tabs-1, XtNorientation, XfwfUpTabs, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, TABHT/4, XmNrightAttachment,XmATTACH_FORM, XmNrightOffset, TABHT/4, XmNbottomAttachment,XmATTACH_OPPOSITE_FORM, XmNbottomOffset, -TABHT, XtNlabels, tab_labels, XtVaTypedArg, XtNlabels, XtRString, tab_labels, strlen(tab_labels) + 1, NULL ); /* add the callback */ XtAddCallback( tabs, XtNactivateCallback, (XtCallbackProc) handle_click, td ); /* another form to hold the controls */ td->form = XtVaCreateWidget( "form", xmFormWidgetClass, parent, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabs, XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL ); /* done */ return td->form; }