Home | History | Annotate | Line # | Download | only in motif_l
      1 /* ***********************************************************************
      2  * This module implements a motif tabbed window widget.
      3  * The source is copied from the Free Widget Foundation
      4  * This file is divided into thse parts
      5  *	o - Conversion routines for the X resource manager
      6  *	o - Routines for drawing rotated text
      7  *	o - A motif widget for tabbed windows
      8  *	o - A driver for the above in the flavor of the xt utilities module
      9  * ***********************************************************************
     10  */
     11 
     12 
     13 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <Xm/Xm.h>
     18 #include <Xm/Form.h>
     19 #include <Xm/RowColumn.h>
     20 #include <X11/StringDefs.h>
     21 #include <X11/IntrinsicP.h>
     22 #if defined(VMS_HOST)
     23 #include <DECW$INCLUDE/shape.h>
     24 #else
     25 #include <X11/extensions/shape.h>
     26 #endif
     27 #include <X11/Xlib.h>
     28 #include <X11/Xutil.h>
     29 #include <X11/Xatom.h>
     30 #include <math.h>
     31 
     32 
     33 /* ***********************************************************************
     35  * "rotated.h"
     36  * ***********************************************************************
     37  */
     38 
     39 /* ************************************************************************ */
     40 
     41 
     42 /* Header file for the `xvertext 5.0' routines.
     43 
     44    Copyright (c) 1993 Alan Richardson (mppa3 (at) uk.ac.sussex.syma) */
     45 
     46 
     47 /* ************************************************************************ */
     48 
     49 #ifndef _XVERTEXT_INCLUDED_
     50 #define _XVERTEXT_INCLUDED_
     51 
     52 
     53 #define XV_VERSION      5.0
     54 #define XV_COPYRIGHT \
     55       "xvertext routines Copyright (c) 1993 Alan Richardson"
     56 
     57 
     58 /* ---------------------------------------------------------------------- */
     59 
     60 
     61 /* text alignment */
     62 
     63 #define NONE             0
     64 #define TLEFT            1
     65 #define TCENTRE          2
     66 #define TRIGHT           3
     67 #define MLEFT            4
     68 #define MCENTRE          5
     69 #define MRIGHT           6
     70 #define BLEFT            7
     71 #define BCENTRE          8
     72 #define BRIGHT           9
     73 
     74 
     75 /* ---------------------------------------------------------------------- */
     76 
     77 /* this shoulf be C++ compliant, thanks to
     78      vlp (at) latina.inesc.pt (Vasco Lopes Paulo) */
     79 
     80 #if defined(__cplusplus) || defined(c_plusplus)
     81 
     82 extern "C" {
     83 static float   XRotVersion(char*, int);
     84 static void    XRotSetMagnification(float);
     85 static void    XRotSetBoundingBoxPad(int);
     86 static int     XRotDrawString(Display*, XFontStruct*, float,
     87                        Drawable, GC, int, int, char*);
     88 static int     XRotDrawImageString(Display*, XFontStruct*, float,
     89                             Drawable, GC, int, int, char*);
     90 static int     XRotDrawAlignedString(Display*, XFontStruct*, float,
     91                               Drawable, GC, int, int, char*, int);
     92 static int     XRotDrawAlignedImageString(Display*, XFontStruct*, float,
     93                                    Drawable, GC, int, int, char*, int);
     94 static XPoint *XRotTextExtents(Display*, XFontStruct*, float,
     95 			int, int, char*, int);
     96 }
     97 
     98 #else
     99 
    100 static float   XRotVersion(char *str, int n);
    101 static void    XRotSetMagnification(float m);
    102 static void    XRotSetBoundingBoxPad(int p);
    103 static int     XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str);
    104 static int     XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str);
    105 static int     XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align);
    106 static int     XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align);
    107 static XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align);
    108 
    109 #endif /* __cplusplus */
    110 
    111 /* ---------------------------------------------------------------------- */
    112 
    113 
    114 #endif /* _XVERTEXT_INCLUDED_ */
    115 
    116 
    117 
    118 
    119 /* ***********************************************************************
    121  * "strarray.h"
    122  * ***********************************************************************
    123  */
    124 
    125 #ifndef _strarray_h_
    126 #define _strarray_h_
    127 /*
    128    StringArray
    129    ===========
    130    The type |StringArray| represents an array of |String|s, with the
    131    proviso that by convention the last member of a |StringArray| is
    132    always a |NULL| pointer. There is a converter that can construct a
    133    |StringArray| from a single string.
    134 
    135 
    136    cvtStringToStringArray
    137    ======================
    138    The converter from |String| to |StringArray| makes a copy of the
    139    passed string and then replaces all occurences of the delimiter
    140    with a nul byte. The |StringArray| is filled with pointers to the
    141    parts of the string.
    142 
    143    The delimiter character is the first character in the string.
    144 
    145 
    146    newStringArray
    147    ==============
    148    The function |newStringArray| makes a copy of a |StringArray|. It
    149    allocates new space for the array itself and for the strings that
    150    it contains.
    151 
    152 
    153    freeStringArray
    154    ===============
    155    |freeStringArray| deallocates the array and all strings it
    156    contains. Note that this works for StringArrays produced with
    157    |newStringArray|, but not for those created by
    158    |cvtStringToStringArray|!
    159 
    160 */
    161 
    162 
    163 typedef String * StringArray;
    164 
    165 static Boolean cvtStringToStringArray(
    166 #if NeedFunctionPrototypes
    167     Display *display,
    168     XrmValuePtr args,
    169     Cardinal *num_args,
    170     XrmValuePtr from,
    171     XrmValuePtr to,
    172     XtPointer *converter_data
    173 #endif
    174 );
    175 
    176 
    177 StringArray newStringArray(
    178 #if NeedFunctionPrototypes
    179     StringArray a
    180 #endif
    181 );
    182 
    183 
    184 void freeStringArray(
    185 #if NeedFunctionPrototypes
    186     StringArray a
    187 #endif
    188 );
    189 
    190 
    191 #endif /* _strarray_h_ */
    192 
    193 
    194 /* ***********************************************************************
    196  * "XmTabs.h"
    197  * ***********************************************************************
    198  */
    199 
    200 /* Generated by wbuild from "XmTabs.w"
    201 ** (generator version Revision: 8.5  of Date: 2001/06/25 15:19:28 )
    202 */
    203 #ifndef _XmTabs_H_
    204 #define _XmTabs_H_
    205 typedef enum {
    206 	    XfwfUpTabs, XfwfDownTabs, XfwfLeftTabs, XfwfRightTabs,
    207 	} TabsOrientation;
    208 
    209 #ifndef XtNorientation
    210 #define XtNorientation "orientation"
    211 #endif
    212 #ifndef XtCOrientation
    213 #define XtCOrientation "Orientation"
    214 #endif
    215 #ifndef XtRTabsOrientation
    216 #define XtRTabsOrientation "TabsOrientation"
    217 #endif
    218 
    219 #ifndef XtNlefttabs
    220 #define XtNlefttabs "lefttabs"
    221 #endif
    222 #ifndef XtCLefttabs
    223 #define XtCLefttabs "Lefttabs"
    224 #endif
    225 #ifndef XtRInt
    226 #define XtRInt "Int"
    227 #endif
    228 
    229 #ifndef XtNrighttabs
    230 #define XtNrighttabs "righttabs"
    231 #endif
    232 #ifndef XtCRighttabs
    233 #define XtCRighttabs "Righttabs"
    234 #endif
    235 #ifndef XtRInt
    236 #define XtRInt "Int"
    237 #endif
    238 
    239 #ifndef XtNlabels
    240 #define XtNlabels "labels"
    241 #endif
    242 #ifndef XtCLabels
    243 #define XtCLabels "Labels"
    244 #endif
    245 #ifndef XtRStringArray
    246 #define XtRStringArray "StringArray"
    247 #endif
    248 
    249 #ifndef XtNtabWidthPercentage
    250 #define XtNtabWidthPercentage "tabWidthPercentage"
    251 #endif
    252 #ifndef XtCTabWidthPercentage
    253 #define XtCTabWidthPercentage "TabWidthPercentage"
    254 #endif
    255 #ifndef XtRInt
    256 #define XtRInt "Int"
    257 #endif
    258 
    259 #ifndef XtNcornerwidth
    260 #define XtNcornerwidth "cornerwidth"
    261 #endif
    262 #ifndef XtCCornerwidth
    263 #define XtCCornerwidth "Cornerwidth"
    264 #endif
    265 #ifndef XtRCardinal
    266 #define XtRCardinal "Cardinal"
    267 #endif
    268 
    269 #ifndef XtNcornerheight
    270 #define XtNcornerheight "cornerheight"
    271 #endif
    272 #ifndef XtCCornerheight
    273 #define XtCCornerheight "Cornerheight"
    274 #endif
    275 #ifndef XtRCardinal
    276 #define XtRCardinal "Cardinal"
    277 #endif
    278 
    279 #ifndef XtNtextmargin
    280 #define XtNtextmargin "textmargin"
    281 #endif
    282 #ifndef XtCTextmargin
    283 #define XtCTextmargin "Textmargin"
    284 #endif
    285 #ifndef XtRInt
    286 #define XtRInt "Int"
    287 #endif
    288 
    289 #ifndef XtNtabcolor
    290 #define XtNtabcolor "tabcolor"
    291 #endif
    292 #ifndef XtCTabcolor
    293 #define XtCTabcolor "Tabcolor"
    294 #endif
    295 #ifndef XtRPixel
    296 #define XtRPixel "Pixel"
    297 #endif
    298 
    299 #ifndef XtNfont
    300 #define XtNfont "font"
    301 #endif
    302 #ifndef XtCFont
    303 #define XtCFont "Font"
    304 #endif
    305 #ifndef XtRFontStruct
    306 #define XtRFontStruct "FontStruct"
    307 #endif
    308 
    309 #ifndef XtNactivateCallback
    310 #define XtNactivateCallback "activateCallback"
    311 #endif
    312 #ifndef XtCActivateCallback
    313 #define XtCActivateCallback "ActivateCallback"
    314 #endif
    315 #ifndef XtRCallback
    316 #define XtRCallback "Callback"
    317 #endif
    318 
    319 typedef struct _XmTabsClassRec *XmTabsWidgetClass;
    320 typedef struct _XmTabsRec *XmTabsWidget;
    321 #endif /*_XmTabs_H_*/
    322 
    323 
    324 /* ***********************************************************************
    326  * "XmTabsP.h"
    327  * ***********************************************************************
    328  */
    329 
    330 /* Generated by wbuild from "XmTabs.w"
    331 ** (generator version Revision: 8.5  of Date: 2001/06/25 15:19:28 )
    332 */
    333 #ifndef _XmTabsP_H_
    334 #define _XmTabsP_H_
    335 
    336 /* raz modified 22 Jul 96 for bluestone */
    337 #include <Xm/XmP.h>
    338 #if ! defined(MGR_ShadowThickness)
    339 #include <Xm/ManagerP.h>
    340 #endif
    341 
    342 typedef void (*border_highlight_Proc)(
    343 #if NeedFunctionPrototypes
    344 void
    345 #endif
    346 );
    347 #define XtInherit_border_highlight ((border_highlight_Proc) _XtInherit)
    348 typedef void (*border_unhighlight_Proc)(
    349 #if NeedFunctionPrototypes
    350 void
    351 #endif
    352 );
    353 #define XtInherit_border_unhighlight ((border_unhighlight_Proc) _XtInherit)
    354 typedef struct {
    355 /* Constraint resources */
    356 /* Private constraint variables */
    357 int dummy;
    358 } XmTabsConstraintPart;
    359 
    360 typedef struct _XmTabsConstraintRec {
    361 XmManagerConstraintPart xmManager;
    362 XmTabsConstraintPart xmTabs;
    363 } XmTabsConstraintRec;
    364 
    365 
    366 typedef struct {
    367 /* methods */
    368 border_highlight_Proc border_highlight;
    369 border_unhighlight_Proc border_unhighlight;
    370 /* class variables */
    371 } XmTabsClassPart;
    372 
    373 typedef struct _XmTabsClassRec {
    374 CoreClassPart core_class;
    375 CompositeClassPart composite_class;
    376 ConstraintClassPart constraint_class;
    377 XmManagerClassPart xmManager_class;
    378 XmTabsClassPart xmTabs_class;
    379 } XmTabsClassRec;
    380 
    381 typedef struct {
    382 /* resources */
    383 TabsOrientation  orientation;
    384 int  lefttabs;
    385 int  righttabs;
    386 StringArray  labels;
    387 int  tabWidthPercentage;
    388 Cardinal  cornerwidth;
    389 Cardinal  cornerheight;
    390 int  textmargin;
    391 Pixel  tabcolor;
    392 XFontStruct * font;
    393 XtCallbackList  activateCallback;
    394 /* private state */
    395 GC  textgc;
    396 GC  topgc;
    397 GC  bottomgc;
    398 GC  backgc;
    399 GC  fillgc;
    400 int * tabwidths;
    401 int * offsets;
    402 } XmTabsPart;
    403 
    404 typedef struct _XmTabsRec {
    405 CorePart core;
    406 CompositePart composite;
    407 ConstraintPart constraint;
    408 XmManagerPart xmManager;
    409 XmTabsPart xmTabs;
    410 } XmTabsRec;
    411 
    412 #endif /* _XmTabsP_H_ */
    413 
    414 
    415 /* ***********************************************************************
    417  * A motif widget for tabbed windows
    418  * ***********************************************************************
    419  */
    420 
    421 static void activate(
    422 #if NeedFunctionPrototypes
    423 Widget,XEvent*,String*,Cardinal*
    424 #endif
    425 );
    426 
    427 static XtActionsRec actionsList[] = {
    428 {"activate", activate},
    429 };
    430 
    431 static char defaultTranslations[] = "\
    432 <Btn1Down>,<Btn1Up>: activate() \n\
    433 ";
    434 static void _resolve_inheritance(
    435 #if NeedFunctionPrototypes
    436 WidgetClass
    437 #endif
    438 );
    439 static void class_initialize(
    440 #if NeedFunctionPrototypes
    441 void
    442 #endif
    443 );
    444 static void initialize(
    445 #if NeedFunctionPrototypes
    446 Widget ,Widget,ArgList ,Cardinal *
    447 #endif
    448 );
    449 static Boolean  set_values(
    450 #if NeedFunctionPrototypes
    451 Widget ,Widget ,Widget,ArgList ,Cardinal *
    452 #endif
    453 );
    454 static void realize(
    455 #if NeedFunctionPrototypes
    456 Widget,XtValueMask *,XSetWindowAttributes *
    457 #endif
    458 );
    459 static void resize(
    460 #if NeedFunctionPrototypes
    461 Widget
    462 #endif
    463 );
    464 static void expose(
    465 #if NeedFunctionPrototypes
    466 Widget,XEvent *,Region
    467 #endif
    468 );
    469 static void border_highlight(
    470 #if NeedFunctionPrototypes
    471 void
    472 #endif
    473 );
    474 static void border_unhighlight(
    475 #if NeedFunctionPrototypes
    476 void
    477 #endif
    478 );
    479 static void destroy(
    480 #if NeedFunctionPrototypes
    481 Widget
    482 #endif
    483 );
    484 #define min(a, b) ((a )<(b )?(a ):(b ))
    485 
    486 
    487 #define abs(x) ((x )<0 ?-(x ):(x ))
    488 
    489 
    490 static void compute_tabsizes(
    491 #if NeedFunctionPrototypes
    492 Widget
    493 #endif
    494 );
    495 static void comp_hor_tab_shape(
    496 #if NeedFunctionPrototypes
    497 Widget,int ,XPoint  p[12],int *,int *,int *
    498 #endif
    499 );
    500 static void comp_ver_tab_shape(
    501 #if NeedFunctionPrototypes
    502 Widget,int ,XPoint  p[12],int *,int *,int *
    503 #endif
    504 );
    505 static void draw_border(
    506 #if NeedFunctionPrototypes
    507 Widget,XPoint  poly[12]
    508 #endif
    509 );
    510 static void draw_hor_tab(
    511 #if NeedFunctionPrototypes
    512 Widget,Region ,int
    513 #endif
    514 );
    515 static void draw_ver_tab(
    516 #if NeedFunctionPrototypes
    517 Widget,Region ,int
    518 #endif
    519 );
    520 static void create_topgc(
    521 #if NeedFunctionPrototypes
    522 Widget
    523 #endif
    524 );
    525 static void create_bottomgc(
    526 #if NeedFunctionPrototypes
    527 Widget
    528 #endif
    529 );
    530 static void create_textgc(
    531 #if NeedFunctionPrototypes
    532 Widget
    533 #endif
    534 );
    535 static void create_fillgc(
    536 #if NeedFunctionPrototypes
    537 Widget
    538 #endif
    539 );
    540 static void create_backgc(
    541 #if NeedFunctionPrototypes
    542 Widget
    543 #endif
    544 );
    545 static void copy_bg(
    546 #if NeedFunctionPrototypes
    547 Widget,int ,XrmValue *
    548 #endif
    549 );
    550 static void set_shape(
    551 #if NeedFunctionPrototypes
    552 Widget
    553 #endif
    554 );
    555 #define done(type, value) do {\
    556 	if (to->addr != NULL) {\
    557 	    if (to->size < sizeof(type)) {\
    558 	        to->size = sizeof(type);\
    559 	        return False;\
    560 	    }\
    561 	    *(type*)(to->addr) = (value);\
    562         } else {\
    563 	    static type static_val;\
    564 	    static_val = (value);\
    565 	    to->addr = (XtPointer)&static_val;\
    566         }\
    567         to->size = sizeof(type);\
    568         return True;\
    569     }while (0 )
    570 
    571 
    572 static Boolean  cvtStringToTabsOrientation(
    573 #if NeedFunctionPrototypes
    574 Display *,XrmValuePtr ,Cardinal *,XrmValuePtr ,XrmValuePtr ,XtPointer *
    575 #endif
    576 );
    577 static Boolean  cvtTabsOrientationToString(
    578 #if NeedFunctionPrototypes
    579 Display *,XrmValuePtr ,Cardinal *,XrmValuePtr ,XrmValuePtr ,XtPointer *
    580 #endif
    581 );
    582 /*ARGSUSED*/
    583 #if NeedFunctionPrototypes
    584 static void compute_tabsizes(Widget self)
    585 #else
    586 static void compute_tabsizes(self)Widget self;
    587 #endif
    588 {
    589     int maxwd, basewidth, delta, i, n = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1;
    590     int sum, len, h, length, breadth, shad = ((XmTabsWidget)self)->xmManager.shadow_thickness;
    591 
    592     if (((XmTabsWidget)self)->xmTabs.offsets) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.offsets);
    593     if (((XmTabsWidget)self)->xmTabs.tabwidths) XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.tabwidths);
    594     ((XmTabsWidget)self)->xmTabs.offsets = (XtPointer) XtMalloc(n * sizeof(*((XmTabsWidget)self)->xmTabs.offsets));
    595     ((XmTabsWidget)self)->xmTabs.tabwidths = (XtPointer) XtMalloc(n * sizeof(*((XmTabsWidget)self)->xmTabs.tabwidths));
    596 
    597     if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs || ((XmTabsWidget)self)->xmTabs.orientation == XfwfDownTabs) {
    598 	length = ((XmTabsWidget)self)->core.width;
    599 	breadth = ((XmTabsWidget)self)->core.height;
    600     } else {
    601 	length = ((XmTabsWidget)self)->core.height;
    602 	breadth = ((XmTabsWidget)self)->core.width;
    603     }
    604     if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage != 0) {		/* Fixed width tabs */
    605 	basewidth = ((XmTabsWidget)self)->xmTabs.tabWidthPercentage * length/100;
    606 	if (n > 1) delta = (length - basewidth)/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs);
    607 	for (i = 0; i < n; i++) {
    608 	    ((XmTabsWidget)self)->xmTabs.tabwidths[i] = basewidth;
    609 	    ((XmTabsWidget)self)->xmTabs.offsets[i] = i * delta;
    610 	}
    611     } else if (((XmTabsWidget)self)->xmTabs.labels == NULL) {		/* Empty tabs */
    612 	basewidth = length/n;
    613 	delta = (length - basewidth)/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs);
    614 	for (i = 0; i < n; i++) {
    615 	    ((XmTabsWidget)self)->xmTabs.tabwidths[i] = basewidth;
    616 	    ((XmTabsWidget)self)->xmTabs.offsets[i] = i * delta;
    617 	}
    618     } else {					/* Variable width tabs */
    619 	sum = 0;
    620 	h = 2 * (((XmTabsWidget)self)->xmTabs.cornerwidth + shad + ((XmTabsWidget)self)->xmTabs.textmargin);
    621 	maxwd = length - n * (shad + ((XmTabsWidget)self)->xmTabs.textmargin);
    622 	for (i = 0; i < n; i++) {
    623 	    len = strlen(((XmTabsWidget)self)->xmTabs.labels[i]);
    624 	    ((XmTabsWidget)self)->xmTabs.tabwidths[i] = min(maxwd, XTextWidth(((XmTabsWidget)self)->xmTabs.font,((XmTabsWidget)self)->xmTabs.labels[i],len) + h);
    625 	    sum += ((XmTabsWidget)self)->xmTabs.tabwidths[i];
    626 	}
    627 	((XmTabsWidget)self)->xmTabs.offsets[0] = 0;
    628 	if (length >= sum)
    629 	    delta = (length - sum)/(n - 1);	/* Between tabs */
    630 	else
    631 	    delta = -((sum - length + n - 2)/(n - 1)); /* Round down! */
    632 	for (i = 1; i < n; i++)
    633 	    ((XmTabsWidget)self)->xmTabs.offsets[i] = ((XmTabsWidget)self)->xmTabs.offsets[i-1] + ((XmTabsWidget)self)->xmTabs.tabwidths[i-1] + delta;
    634     }
    635 }
    636 /*ARGSUSED*/
    637 #if NeedFunctionPrototypes
    638 static void comp_hor_tab_shape(Widget self,int  i,XPoint  p[12],int * x0,int * x1,int * midy)
    639 #else
    640 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;
    641 #endif
    642 {
    643     int shad = ((XmTabsWidget)self)->xmManager.shadow_thickness;
    644     int k = min(((XmTabsWidget)self)->xmTabs.cornerheight, (((XmTabsWidget)self)->core.height - shad)/2);
    645     /*
    646      *                4 o-------------o 5
    647      *                 /               \
    648      *              3 o                 o 6
    649      *                |                 |
    650      *              2 o                 o 7
    651      *             1 /                   \ 8
    652      *   0 o--------o                     o--------o 9
    653      *  11 o---------------------------------------o 10
    654      *
    655      *  11 o---------------------------------------o 10
    656      *   0 o--------o                     o--------o 9
    657      *             1 \                   / 8
    658      *              2 o                 o 7
    659      *                |                 |
    660      *              3 o                 o 6
    661      *                 \               /
    662      *                4 o-------------o 5
    663      */
    664     p[0].x = 0;
    665     p[1].x = ((XmTabsWidget)self)->xmTabs.offsets[i];
    666     p[2].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth;
    667     p[3].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth;
    668     p[4].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth;
    669     p[5].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth;
    670     p[6].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth;
    671     p[7].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth;
    672     p[8].x = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i];
    673     p[9].x = ((XmTabsWidget)self)->core.width;
    674     p[10].x = ((XmTabsWidget)self)->core.width;
    675     p[11].x = 0;
    676 
    677     if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs) {
    678 	p[0].y = ((XmTabsWidget)self)->core.height - shad;
    679 	p[1].y = ((XmTabsWidget)self)->core.height - shad;
    680 	p[2].y = ((XmTabsWidget)self)->core.height - shad - k;
    681 	p[3].y = k;
    682 	p[4].y = 0;
    683 	p[5].y = 0;
    684 	p[6].y = k;
    685 	p[7].y = ((XmTabsWidget)self)->core.height - shad - k;
    686 	p[8].y = ((XmTabsWidget)self)->core.height - shad;
    687 	p[9].y = ((XmTabsWidget)self)->core.height - shad;
    688 	p[10].y = ((XmTabsWidget)self)->core.height;
    689 	p[11].y = ((XmTabsWidget)self)->core.height;
    690     } else {
    691 	p[0].y = shad;
    692 	p[1].y = shad;
    693 	p[2].y = shad + k;
    694 	p[3].y = ((XmTabsWidget)self)->core.height - k;
    695 	p[4].y = ((XmTabsWidget)self)->core.height;
    696 	p[5].y = ((XmTabsWidget)self)->core.height;
    697 	p[6].y = ((XmTabsWidget)self)->core.height - k;
    698 	p[7].y = shad + k;
    699 	p[8].y = shad;
    700 	p[9].y = shad;
    701 	p[10].y = 0;
    702 	p[11].y = 0;
    703     }
    704     *x0 = p[4].x;
    705     *x1 = p[5].x;
    706     *midy = (p[1].y + p[4].y)/2;
    707 }
    708 /*ARGSUSED*/
    709 #if NeedFunctionPrototypes
    710 static void comp_ver_tab_shape(Widget self,int  i,XPoint  p[12],int * y0,int * y1,int * midx)
    711 #else
    712 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;
    713 #endif
    714 {
    715     int shad = ((XmTabsWidget)self)->xmManager.shadow_thickness;
    716     int k = min(((XmTabsWidget)self)->xmTabs.cornerheight, (((XmTabsWidget)self)->core.width - shad)/2);
    717     /*
    718      *       0 o_o 11  11 o_o 0
    719      *         | |        | |
    720      *       1 o |        | o 1
    721      *     3 2/  |        |  \2 3
    722      *     o-o   |        |   o-o
    723      *    /      |        |      \
    724      * 4 o       |        |       o 4
    725      *   |       |        |       |
    726      * 5 o       |        |       o 5
    727      *    \      |        |      /
    728      *     o-o   |        |   o-o
    729      *     6 7\  |        |  /7 6
    730      *       8 o |        | o 8
    731      *         | |        | |
    732      *       9 o_o 10  10 o_o 9
    733      */
    734     if (((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs) {
    735 	p[0].x = ((XmTabsWidget)self)->core.width - shad;
    736 	p[1].x = ((XmTabsWidget)self)->core.width - shad;
    737 	p[2].x = ((XmTabsWidget)self)->core.width - shad - k;
    738 	p[3].x = k;
    739 	p[4].x = 0;
    740 	p[5].x = 0;
    741 	p[6].x = k;
    742 	p[7].x = ((XmTabsWidget)self)->core.width - shad - k;
    743 	p[8].x = ((XmTabsWidget)self)->core.width - shad;
    744 	p[9].x = ((XmTabsWidget)self)->core.width - shad;
    745 	p[10].x = ((XmTabsWidget)self)->core.width;
    746 	p[11].x = ((XmTabsWidget)self)->core.width;
    747     } else {
    748 	p[0].x = shad;
    749 	p[1].x = shad;
    750 	p[2].x = shad + k;
    751 	p[3].x = ((XmTabsWidget)self)->core.width - k;
    752 	p[4].x = ((XmTabsWidget)self)->core.width;
    753 	p[5].x = ((XmTabsWidget)self)->core.width;
    754 	p[6].x = ((XmTabsWidget)self)->core.width - k;
    755 	p[7].x = shad + k;
    756 	p[8].x = shad;
    757 	p[9].x = shad;
    758 	p[10].x = 0;
    759 	p[11].x = 0;
    760     }
    761     p[0].y = 0;
    762     p[1].y = ((XmTabsWidget)self)->xmTabs.offsets[i];
    763     p[2].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth;
    764     p[3].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.cornerwidth;
    765     p[4].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth;
    766     p[5].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - 2 * ((XmTabsWidget)self)->xmTabs.cornerwidth;
    767     p[6].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth;
    768     p[7].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i] - ((XmTabsWidget)self)->xmTabs.cornerwidth;
    769     p[8].y = ((XmTabsWidget)self)->xmTabs.offsets[i] + ((XmTabsWidget)self)->xmTabs.tabwidths[i];
    770     p[9].y = ((XmTabsWidget)self)->core.height;
    771     p[10].y = ((XmTabsWidget)self)->core.height;
    772     p[11].y = 0;
    773     *y0 = p[4].y;
    774     *y1 = p[5].y;
    775     *midx = (p[1].x + p[4].x)/2;
    776 }
    777 /*ARGSUSED*/
    778 #if NeedFunctionPrototypes
    779 static void draw_border(Widget self,XPoint  poly[12])
    780 #else
    781 static void draw_border(self,poly)Widget self;XPoint  poly[12];
    782 #endif
    783 {
    784     Display *dpy = XtDisplay(self);
    785     Window win = XtWindow(self);
    786 
    787     if (((XmTabsWidget)self)->xmTabs.orientation == XfwfUpTabs) {
    788 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly, 6, CoordModeOrigin);
    789 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly + 5, 4, CoordModeOrigin);
    790 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly + 8, 2, CoordModeOrigin);
    791     } else {
    792 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly, 2, CoordModeOrigin);
    793 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.topgc, poly + 1, 4, CoordModeOrigin);
    794 	XDrawLines(dpy, win, ((XmTabsWidget)self)->xmTabs.bottomgc, poly + 4, 6, CoordModeOrigin);
    795     }
    796 }
    797 /*ARGSUSED*/
    798 #if NeedFunctionPrototypes
    799 static void draw_hor_tab(Widget self,Region  region,int  i)
    800 #else
    801 static void draw_hor_tab(self,region,i)Widget self;Region  region;int  i;
    802 #endif
    803 {
    804     XPoint p[12];
    805     Display *dpy = XtDisplay(self);
    806     Window win = XtWindow(self);
    807     Region clip;
    808     int x0, x1, midy;
    809 
    810     comp_hor_tab_shape(self, i, p, &x0, &x1, &midy);
    811     clip = XPolygonRegion(p, XtNumber(p), WindingRule);
    812     if (region) XIntersectRegion(clip, region, clip);
    813     if (XEmptyRegion(clip)) return;
    814 
    815     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.textgc, clip);
    816     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.topgc, clip);
    817     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.bottomgc, clip);
    818     if (i == ((XmTabsWidget)self)->xmTabs.lefttabs) {
    819 	XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.backgc, clip);
    820 	XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.backgc,
    821 		     p, XtNumber(p), Convex, CoordModeOrigin);
    822     } else {
    823 	XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.fillgc, clip);
    824 	XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.fillgc,
    825 		     p, XtNumber(p), Convex, CoordModeOrigin);
    826     }
    827     if (((XmTabsWidget)self)->xmTabs.labels) {
    828 	int w, y, x, len = strlen(((XmTabsWidget)self)->xmTabs.labels[i]);
    829 	y = midy - (((XmTabsWidget)self)->xmTabs.font->ascent + ((XmTabsWidget)self)->xmTabs.font->descent)/2 + ((XmTabsWidget)self)->xmTabs.font->ascent;
    830 	w = XTextWidth(((XmTabsWidget)self)->xmTabs.font, ((XmTabsWidget)self)->xmTabs.labels[i], len);
    831 	if (i == ((XmTabsWidget)self)->xmTabs.lefttabs
    832 	    || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage <= 100/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1))
    833 	    x = (x0 + x1 - w)/2;		/* Centered text */
    834 	else if (i < ((XmTabsWidget)self)->xmTabs.lefttabs)
    835 	    x = x0 + ((XmTabsWidget)self)->xmTabs.textmargin;		/* Left aligned text */
    836 	else
    837 	    x = x1 - ((XmTabsWidget)self)->xmTabs.textmargin - w;		/* Right aligned text */
    838 	XDrawString(dpy, win, ((XmTabsWidget)self)->xmTabs.textgc, x, y, ((XmTabsWidget)self)->xmTabs.labels[i], len);
    839     }
    840     draw_border(self, p);
    841     XDestroyRegion(clip);
    842 }
    843 /*ARGSUSED*/
    844 #if NeedFunctionPrototypes
    845 static void draw_ver_tab(Widget self,Region  region,int  i)
    846 #else
    847 static void draw_ver_tab(self,region,i)Widget self;Region  region;int  i;
    848 #endif
    849 {
    850     Display *dpy = XtDisplay(self);
    851     Window win = XtWindow(self);
    852     XPoint p[12];
    853     Region clip;
    854     int y0, y1, midx;
    855 
    856     comp_ver_tab_shape(self, i, p, &y0, &y1, &midx);
    857     clip = XPolygonRegion(p, XtNumber(p), WindingRule);
    858     if (region) XIntersectRegion(clip, region, clip);
    859     if (XEmptyRegion(clip)) return;
    860 
    861     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.textgc, clip);
    862     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.topgc, clip);
    863     XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.bottomgc, clip);
    864     if (i == ((XmTabsWidget)self)->xmTabs.lefttabs) {
    865 	XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.backgc, clip);
    866 	XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.backgc,
    867 		     p, XtNumber(p), Convex, CoordModeOrigin);
    868     } else {
    869 	XSetRegion(dpy, ((XmTabsWidget)self)->xmTabs.fillgc, clip);
    870 	XFillPolygon(dpy, win, ((XmTabsWidget)self)->xmTabs.fillgc,
    871 		     p, XtNumber(p), Convex, CoordModeOrigin);
    872     }
    873     if (((XmTabsWidget)self)->xmTabs.labels) {
    874 	int y, align;
    875 	float angle = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? 90.0 : -90.0;
    876 	if (i == ((XmTabsWidget)self)->xmTabs.lefttabs
    877 	    || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage <= 100/(((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1)) {
    878 	    y = (y0 + y1)/2;
    879 	    align = MCENTRE;
    880 	} else if (i < ((XmTabsWidget)self)->xmTabs.lefttabs) {
    881 	    y = y0 + ((XmTabsWidget)self)->xmTabs.textmargin;
    882 	    align = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? MRIGHT : MLEFT;
    883 	} else {
    884 	    y = y1 - ((XmTabsWidget)self)->xmTabs.textmargin;
    885 	    align = ((XmTabsWidget)self)->xmTabs.orientation == XfwfLeftTabs ? MLEFT : MRIGHT;
    886 	}
    887 	XRotDrawAlignedString
    888 	    (dpy, ((XmTabsWidget)self)->xmTabs.font, angle, win, ((XmTabsWidget)self)->xmTabs.textgc, midx, y, ((XmTabsWidget)self)->xmTabs.labels[i], align);
    889     }
    890     draw_border(self, p);
    891     XDestroyRegion(clip);
    892 }
    893 /*ARGSUSED*/
    894 #if NeedFunctionPrototypes
    895 static void create_topgc(Widget self)
    896 #else
    897 static void create_topgc(self)Widget self;
    898 #endif
    899 {
    900     XtGCMask mask = GCForeground | GCLineWidth;
    901     XGCValues values;
    902 
    903     if (((XmTabsWidget)self)->xmTabs.topgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.topgc);
    904     values.foreground = ((XmTabsWidget)self)->xmManager.top_shadow_color;
    905     values.line_width = 2 * ((XmTabsWidget)self)->xmManager.shadow_thickness;
    906     ((XmTabsWidget)self)->xmTabs.topgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)),
    907 		       mask, &values);
    908 }
    909 /*ARGSUSED*/
    910 #if NeedFunctionPrototypes
    911 static void create_bottomgc(Widget self)
    912 #else
    913 static void create_bottomgc(self)Widget self;
    914 #endif
    915 {
    916     XtGCMask mask = GCForeground | GCLineWidth;
    917     XGCValues values;
    918 
    919     if (((XmTabsWidget)self)->xmTabs.bottomgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.bottomgc);
    920     values.foreground = ((XmTabsWidget)self)->xmManager.bottom_shadow_color;
    921     values.line_width = 2 * ((XmTabsWidget)self)->xmManager.shadow_thickness;
    922     ((XmTabsWidget)self)->xmTabs.bottomgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)),
    923 			  mask, &values);
    924 }
    925 /*ARGSUSED*/
    926 #if NeedFunctionPrototypes
    927 static void create_textgc(Widget self)
    928 #else
    929 static void create_textgc(self)Widget self;
    930 #endif
    931 {
    932     XtGCMask mask = GCForeground | GCFont;
    933     XGCValues values;
    934 
    935     if (((XmTabsWidget)self)->xmTabs.textgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.textgc);
    936     values.foreground = ((XmTabsWidget)self)->xmManager.foreground;
    937     values.font = ((XmTabsWidget)self)->xmTabs.font->fid;
    938     ((XmTabsWidget)self)->xmTabs.textgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)),
    939 			mask, &values);
    940 }
    941 /*ARGSUSED*/
    942 #if NeedFunctionPrototypes
    943 static void create_fillgc(Widget self)
    944 #else
    945 static void create_fillgc(self)Widget self;
    946 #endif
    947 {
    948     XtGCMask mask = GCForeground;
    949     XGCValues values;
    950 
    951     if (((XmTabsWidget)self)->xmTabs.fillgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.fillgc);
    952     values.foreground = ((XmTabsWidget)self)->xmTabs.tabcolor;
    953     ((XmTabsWidget)self)->xmTabs.fillgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)),
    954 			mask, &values);
    955 }
    956 /*ARGSUSED*/
    957 #if NeedFunctionPrototypes
    958 static void create_backgc(Widget self)
    959 #else
    960 static void create_backgc(self)Widget self;
    961 #endif
    962 {
    963     XtGCMask mask = GCForeground;
    964     XGCValues values;
    965 
    966     if (((XmTabsWidget)self)->xmTabs.backgc != NULL) XFreeGC(XtDisplay(self), ((XmTabsWidget)self)->xmTabs.backgc);
    967     values.foreground = ((XmTabsWidget)self)->core.background_pixel;
    968     ((XmTabsWidget)self)->xmTabs.backgc = XCreateGC(XtDisplay(self), RootWindowOfScreen(XtScreen(self)),
    969 			mask, &values);
    970 }
    971 /*ARGSUSED*/
    972 #if NeedFunctionPrototypes
    973 static void copy_bg(Widget self,int  offset,XrmValue * value)
    974 #else
    975 static void copy_bg(self,offset,value)Widget self;int  offset;XrmValue * value;
    976 #endif
    977 {
    978     value->addr = (XtPointer) &((XmTabsWidget)self)->core.background_pixel;
    979 }
    980 /*ARGSUSED*/
    981 #if NeedFunctionPrototypes
    982 static void set_shape(Widget self)
    983 #else
    984 static void set_shape(self)Widget self;
    985 #endif
    986 {
    987     int x0, x1, midy, y0, y1, midx, i;
    988     Region region, clip;
    989     XPoint poly[12];
    990 
    991     if (! XtIsRealized(self)) return;
    992 
    993     region = XCreateRegion();
    994 
    995     switch (((XmTabsWidget)self)->xmTabs.orientation) {
    996     case XfwfUpTabs:
    997     case XfwfDownTabs:
    998 	for (i = 0; i <= ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i++) {
    999 	    comp_hor_tab_shape(self, i, poly, &x0, &x1, &midy);
   1000 	    clip = XPolygonRegion(poly, XtNumber(poly), WindingRule);
   1001 	    XUnionRegion(region, clip, region);
   1002 	    XDestroyRegion(clip);
   1003 	}
   1004 	break;
   1005     case XfwfLeftTabs:
   1006     case XfwfRightTabs:
   1007 	for (i = 0; i <= ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i++) {
   1008 	    comp_ver_tab_shape(self, i, poly, &y0, &y1, &midx);
   1009 	    clip = XPolygonRegion(poly, XtNumber(poly), WindingRule);
   1010 	    XUnionRegion(region, clip, region);
   1011 	    XDestroyRegion(clip);
   1012 	}
   1013 	break;
   1014     }
   1015     XShapeCombineRegion(XtDisplay(self), XtWindow(self), ShapeBounding,
   1016 			0, 0, region, ShapeSet);
   1017     XDestroyRegion(region);
   1018 }
   1019 
   1020 /*ARGSUSED*/
   1021 #if NeedFunctionPrototypes
   1022 static Boolean  cvtStringToTabsOrientation(Display * display,XrmValuePtr  args,Cardinal * num_args,XrmValuePtr  from,XrmValuePtr  to,XtPointer * converter_data)
   1023 #else
   1024 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;
   1025 #endif
   1026 {
   1027     TabsOrientation a = XfwfUpTabs;
   1028     char *s = (char*) from->addr;
   1029     static struct {
   1030 	char *name;
   1031 	TabsOrientation orient;
   1032     } strings[] = {
   1033     	{ "up",  XfwfUpTabs },
   1034     	{ "uptabs",  XfwfUpTabs },
   1035     	{ "down",  XfwfDownTabs },
   1036     	{ "downtabs",  XfwfDownTabs },
   1037     	{ "left",  XfwfLeftTabs },
   1038     	{ "lefttabs",  XfwfLeftTabs },
   1039     	{ "right",  XfwfRightTabs },
   1040     	{ "righttabs",  XfwfRightTabs },
   1041     };
   1042     int i;
   1043 
   1044     if (*num_args != 0)
   1045 	XtAppErrorMsg
   1046 	    (XtDisplayToApplicationContext(display),
   1047 	     "cvtStringToTabsOrientation", "wrongParameters", "XtToolkitError",
   1048 	     "String to TabsOrientation conversion needs no arguments",
   1049 	     (String*) NULL, (Cardinal*) NULL);
   1050 
   1051     for (i=0; i<XtNumber(strings); i++)
   1052 	if ( strcmp( s, strings[i].name ) == 0 ) {
   1053 	    a |= strings[i].orient;
   1054 	    break;
   1055     }
   1056 
   1057     if ( i >= XtNumber(strings) )
   1058 	XtDisplayStringConversionWarning(display, s, "TabsOrientation");
   1059     done(TabsOrientation, a);
   1060 }
   1061 /*ARGSUSED*/
   1062 #if NeedFunctionPrototypes
   1063 static Boolean  cvtTabsOrientationToString(Display * display,XrmValuePtr  args,Cardinal * num_args,XrmValuePtr  from,XrmValuePtr  to,XtPointer * converter_data)
   1064 #else
   1065 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;
   1066 #endif
   1067 {
   1068     TabsOrientation *a = (TabsOrientation*) from->addr;
   1069 
   1070     if (*num_args != 0)
   1071 	XtAppErrorMsg
   1072 	    (XtDisplayToApplicationContext(display),
   1073 	     "cvtTabsOrientationToString", "wrongParameters", "XtToolkitError",
   1074 	     "TabsOrientation to String conversion needs no arguments",
   1075 	     (String*) NULL, (Cardinal*) NULL);
   1076     switch (*a) {
   1077     case XfwfUpTabs: done(String, "up");
   1078     case XfwfDownTabs: done(String, "down");
   1079     case XfwfLeftTabs: done(String, "left");
   1080     case XfwfRightTabs: done(String, "right");
   1081     }
   1082     XtAppErrorMsg
   1083 	(XtDisplayToApplicationContext(display),
   1084 	 "cvtTabsOrientationToString", "illParameters", "XtToolkitError",
   1085 	     "TabsOrientation to String conversion got illegal argument",
   1086 	     (String*) NULL, (Cardinal*) NULL);
   1087     return TRUE;
   1088 }
   1089 
   1090 static XtResource resources[] = {
   1091 {XtNorientation,XtCOrientation,XtRTabsOrientation,sizeof(((XmTabsRec*)NULL)->xmTabs.orientation),XtOffsetOf(XmTabsRec,xmTabs.orientation),XtRImmediate,(XtPointer)XfwfUpTabs },
   1092 {XtNlefttabs,XtCLefttabs,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.lefttabs),XtOffsetOf(XmTabsRec,xmTabs.lefttabs),XtRImmediate,(XtPointer)0 },
   1093 {XtNrighttabs,XtCRighttabs,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.righttabs),XtOffsetOf(XmTabsRec,xmTabs.righttabs),XtRImmediate,(XtPointer)0 },
   1094 {XtNlabels,XtCLabels,XtRStringArray,sizeof(((XmTabsRec*)NULL)->xmTabs.labels),XtOffsetOf(XmTabsRec,xmTabs.labels),XtRImmediate,(XtPointer)NULL },
   1095 {XtNtabWidthPercentage,XtCTabWidthPercentage,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.tabWidthPercentage),XtOffsetOf(XmTabsRec,xmTabs.tabWidthPercentage),XtRImmediate,(XtPointer)50 },
   1096 {XtNcornerwidth,XtCCornerwidth,XtRCardinal,sizeof(((XmTabsRec*)NULL)->xmTabs.cornerwidth),XtOffsetOf(XmTabsRec,xmTabs.cornerwidth),XtRImmediate,(XtPointer)3 },
   1097 {XtNcornerheight,XtCCornerheight,XtRCardinal,sizeof(((XmTabsRec*)NULL)->xmTabs.cornerheight),XtOffsetOf(XmTabsRec,xmTabs.cornerheight),XtRImmediate,(XtPointer)3 },
   1098 {XtNtextmargin,XtCTextmargin,XtRInt,sizeof(((XmTabsRec*)NULL)->xmTabs.textmargin),XtOffsetOf(XmTabsRec,xmTabs.textmargin),XtRImmediate,(XtPointer)3 },
   1099 {XtNtabcolor,XtCTabcolor,XtRPixel,sizeof(((XmTabsRec*)NULL)->xmTabs.tabcolor),XtOffsetOf(XmTabsRec,xmTabs.tabcolor),XtRCallProc,(XtPointer)copy_bg },
   1100 {XtNfont,XtCFont,XtRFontStruct,sizeof(((XmTabsRec*)NULL)->xmTabs.font),XtOffsetOf(XmTabsRec,xmTabs.font),XtRString,(XtPointer)XtDefaultFont },
   1101 {XtNactivateCallback,XtCActivateCallback,XtRCallback,sizeof(((XmTabsRec*)NULL)->xmTabs.activateCallback),XtOffsetOf(XmTabsRec,xmTabs.activateCallback),XtRImmediate,(XtPointer)NULL },
   1102 };
   1103 
   1104 XmTabsClassRec xmTabsClassRec = {
   1105 { /* core_class part */
   1106 /* superclass   	*/  (WidgetClass) &xmManagerClassRec,
   1107 /* class_name   	*/  "XmTabs",
   1108 /* widget_size  	*/  sizeof(XmTabsRec),
   1109 /* class_initialize 	*/  class_initialize,
   1110 /* class_part_initialize*/  _resolve_inheritance,
   1111 /* class_inited 	*/  FALSE,
   1112 /* initialize   	*/  initialize,
   1113 /* initialize_hook 	*/  NULL,
   1114 /* realize      	*/  realize,
   1115 /* actions      	*/  actionsList,
   1116 /* num_actions  	*/  1,
   1117 /* resources    	*/  resources,
   1118 /* num_resources 	*/  11,
   1119 /* xrm_class    	*/  NULLQUARK,
   1120 /* compres_motion 	*/  True ,
   1121 /* compress_exposure 	*/  XtExposeCompressMultiple ,
   1122 /* compress_enterleave 	*/  True ,
   1123 /* visible_interest 	*/  False ,
   1124 /* destroy      	*/  destroy,
   1125 /* resize       	*/  resize,
   1126 /* expose       	*/  expose,
   1127 /* set_values   	*/  set_values,
   1128 /* set_values_hook 	*/  NULL,
   1129 /* set_values_almost 	*/  XtInheritSetValuesAlmost,
   1130 /* get_values+hook 	*/  NULL,
   1131 /* accept_focus 	*/  XtInheritAcceptFocus,
   1132 /* version      	*/  XtVersion,
   1133 /* callback_private 	*/  NULL,
   1134 /* tm_table      	*/  defaultTranslations,
   1135 /* query_geometry 	*/  XtInheritQueryGeometry,
   1136 /* display_acceleator 	*/  XtInheritDisplayAccelerator,
   1137 /* extension    	*/  NULL
   1138 },
   1139 { /* composite_class part */
   1140 XtInheritGeometryManager,
   1141 XtInheritChangeManaged,
   1142 XtInheritInsertChild,
   1143 XtInheritDeleteChild,
   1144 NULL
   1145 },
   1146 { /* constraint_class part */
   1147 /* constraint_resources     */  NULL,
   1148 /* num_constraint_resources */  0,
   1149 /* constraint_size          */  sizeof(XmTabsConstraintRec),
   1150 /* constraint_initialize    */  NULL,
   1151 /* constraint_destroy       */  NULL,
   1152 /* constraint_set_values    */  NULL,
   1153 /* constraint_extension     */  NULL
   1154 },
   1155 { /* XmManager class part */
   1156 #define manager_extension extension
   1157 /* translations                 */  XtInheritTranslations ,
   1158 /* syn_resources                */  NULL ,
   1159 /* num_syn_resources            */  0 ,
   1160 /* syn_constraint_resources     */  NULL ,
   1161 /* num_syn_constraint_resources */  0 ,
   1162 /* parent_process               */  XmInheritParentProcess,
   1163 /* manager_extension            */  NULL ,
   1164 },
   1165 { /* XmTabs_class part */
   1166 border_highlight,
   1167 border_unhighlight,
   1168 },
   1169 };
   1170 WidgetClass xmTabsWidgetClass = (WidgetClass) &xmTabsClassRec;
   1171 /*ARGSUSED*/
   1172 static void activate(Widget self, XEvent *event, String *params, Cardinal *num_params)
   1173 {
   1174     int x0, x1, dummy, i, x, y;
   1175     XPoint poly[12];
   1176 
   1177     switch (((XmTabsWidget)self)->xmTabs.orientation) {
   1178     case XfwfUpTabs:
   1179     case XfwfDownTabs:
   1180 	x = event->xbutton.x;
   1181 	comp_hor_tab_shape(self, ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1182 	if (x0 <= x && x < x1) {
   1183 	    XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) 0);
   1184 	    return;
   1185 	}
   1186 	for (i = -1; i >= -((XmTabsWidget)self)->xmTabs.lefttabs; i--) {
   1187 	    comp_hor_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1188 	    if (x0 <= x && x < x1) {
   1189 		XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i);
   1190 		return;
   1191 	    }
   1192 	}
   1193 	for (i = 1; i <= ((XmTabsWidget)self)->xmTabs.righttabs; i++) {
   1194 	    comp_hor_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1195 	    if (x0 <= x && x < x1) {
   1196 		XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i);
   1197 		return;
   1198 	    }
   1199 	}
   1200 	break;
   1201     case XfwfLeftTabs:
   1202     case XfwfRightTabs:
   1203 	y = event->xbutton.y;
   1204 	comp_ver_tab_shape(self, ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1205 	if (x0 <= y && y < x1) {
   1206 	    XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) 0);
   1207 	    return;
   1208 	}
   1209 	for (i = -1; i >= -((XmTabsWidget)self)->xmTabs.lefttabs; i--) {
   1210 	    comp_ver_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1211 	    if (x0 <= y && y < x1) {
   1212 		XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i);
   1213 		return;
   1214 	    }
   1215 	}
   1216 	for (i = 1; i <= ((XmTabsWidget)self)->xmTabs.righttabs; i++) {
   1217 	    comp_ver_tab_shape(self, i + ((XmTabsWidget)self)->xmTabs.lefttabs, poly, &x0, &x1, &dummy);
   1218 	    if (x0 <= y && y < x1) {
   1219 		XtCallCallbackList(self, ((XmTabsWidget)self)->xmTabs.activateCallback, (XtPointer) i);
   1220 		return;
   1221 	    }
   1222 	}
   1223 	break;
   1224     }
   1225 }
   1226 
   1227 static void _resolve_inheritance(WidgetClass class)
   1228 {
   1229   XmTabsWidgetClass c = (XmTabsWidgetClass) class;
   1230   XmTabsWidgetClass super;
   1231   static CompositeClassExtensionRec extension_rec = {
   1232     NULL, NULLQUARK, XtCompositeExtensionVersion,
   1233     sizeof(CompositeClassExtensionRec), True};
   1234   CompositeClassExtensionRec *ext;
   1235   ext = (XtPointer)XtMalloc(sizeof(*ext));
   1236   *ext = extension_rec;
   1237   ext->next_extension = c->composite_class.extension;
   1238   c->composite_class.extension = ext;
   1239   if (class == xmTabsWidgetClass) return;
   1240   super = (XmTabsWidgetClass)class->core_class.superclass;
   1241   if (c->xmTabs_class.border_highlight == XtInherit_border_highlight)
   1242     c->xmTabs_class.border_highlight = super->xmTabs_class.border_highlight;
   1243   if (c->xmTabs_class.border_unhighlight == XtInherit_border_unhighlight)
   1244     c->xmTabs_class.border_unhighlight = super->xmTabs_class.border_unhighlight;
   1245 }
   1246 /*ARGSUSED*/
   1247 #if NeedFunctionPrototypes
   1248 static void class_initialize(void)
   1249 #else
   1250 static void class_initialize(void)
   1251 #endif
   1252 {
   1253     XtSetTypeConverter(XtRString, "StringArray",
   1254 		       cvtStringToStringArray,
   1255 		       NULL, 0, XtCacheNone, NULL);
   1256     XtSetTypeConverter(XtRString, "TabsOrientation",
   1257 		       cvtStringToTabsOrientation,
   1258 		       NULL, 0, XtCacheNone, NULL);
   1259     XtSetTypeConverter("TabsOrientation", XtRString,
   1260 		       cvtTabsOrientationToString,
   1261 		       NULL, 0, XtCacheNone, NULL);
   1262 }
   1263 /*ARGSUSED*/
   1264 #if NeedFunctionPrototypes
   1265 static void initialize(Widget  request,Widget self,ArgList  args,Cardinal * num_args)
   1266 #else
   1267 static void initialize(request,self,args,num_args)Widget  request;Widget self;ArgList  args;Cardinal * num_args;
   1268 #endif
   1269 {
   1270     String *h;
   1271     int i;
   1272 
   1273     ((XmTabsWidget)self)->xmManager.traversal_on = FALSE;
   1274     ((XmTabsWidget)self)->xmTabs.topgc = NULL;
   1275     create_topgc(self);
   1276     ((XmTabsWidget)self)->xmTabs.bottomgc = NULL;
   1277     create_bottomgc(self);
   1278     ((XmTabsWidget)self)->xmTabs.textgc = NULL;
   1279     create_textgc(self);
   1280     ((XmTabsWidget)self)->xmTabs.fillgc = NULL;
   1281     create_fillgc(self);
   1282     ((XmTabsWidget)self)->xmTabs.backgc = NULL;
   1283     create_backgc(self);
   1284     if (((XmTabsWidget)self)->xmTabs.labels) {
   1285 	h = (String*) XtMalloc((((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1) * sizeof(*h));
   1286 	for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--)
   1287 	    h[i] = XtNewString(((XmTabsWidget)self)->xmTabs.labels[i]);
   1288 	((XmTabsWidget)self)->xmTabs.labels = h;
   1289     }
   1290     if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage < 0 || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage > 100) {
   1291 	XtAppWarning(XtWidgetToApplicationContext(self),
   1292 		     "tabWidthPercentage out of range; reset to 50");
   1293 	((XmTabsWidget)self)->xmTabs.tabWidthPercentage = 50;
   1294     }
   1295     ((XmTabsWidget)self)->xmTabs.offsets = NULL;
   1296     ((XmTabsWidget)self)->xmTabs.tabwidths = NULL;
   1297     compute_tabsizes(self);
   1298 }
   1299 /*ARGSUSED*/
   1300 #if NeedFunctionPrototypes
   1301 static Boolean  set_values(Widget  old,Widget  request,Widget self,ArgList  args,Cardinal * num_args)
   1302 #else
   1303 static Boolean  set_values(old,request,self,args,num_args)Widget  old;Widget  request;Widget self;ArgList  args;Cardinal * num_args;
   1304 #endif
   1305 {
   1306     Bool redraw = FALSE, resize_labels = FALSE;
   1307     String *h;
   1308     int i;
   1309 
   1310     if (((XmTabsWidget)self)->core.background_pixel != ((XmTabsWidget)old)->core.background_pixel
   1311 	|| ((XmTabsWidget)self)->core.background_pixmap != ((XmTabsWidget)old)->core.background_pixmap
   1312 	|| ((XmTabsWidget)self)->xmManager.shadow_thickness != ((XmTabsWidget)old)->xmManager.shadow_thickness) {
   1313 	create_topgc(self);
   1314 	create_bottomgc(self);
   1315 	create_backgc(self);
   1316     }
   1317     if (((XmTabsWidget)self)->xmManager.foreground != ((XmTabsWidget)old)->xmManager.foreground || ((XmTabsWidget)self)->xmTabs.font != ((XmTabsWidget)old)->xmTabs.font) {
   1318 	create_textgc(self);
   1319 	redraw = TRUE;
   1320     }
   1321     if (((XmTabsWidget)self)->xmTabs.tabcolor != ((XmTabsWidget)old)->xmTabs.tabcolor) {
   1322 	create_fillgc(self);
   1323 	redraw = TRUE;
   1324     }
   1325     if ((((XmTabsWidget)self)->xmTabs.textmargin != ((XmTabsWidget)old)->xmTabs.textmargin && ((XmTabsWidget)self)->xmTabs.tabWidthPercentage == 0)
   1326 	|| ((XmTabsWidget)self)->xmTabs.cornerwidth != ((XmTabsWidget)old)->xmTabs.cornerwidth
   1327 	|| ((XmTabsWidget)self)->xmTabs.cornerheight != ((XmTabsWidget)old)->xmTabs.cornerheight) {
   1328 	resize_labels = TRUE;
   1329     }
   1330     if (((XmTabsWidget)self)->xmTabs.labels != ((XmTabsWidget)old)->xmTabs.labels) {
   1331 	if (((XmTabsWidget)self)->xmTabs.labels) {
   1332 	    h = (String*) XtMalloc((((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs + 1) * sizeof(*h));
   1333 	    for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--)
   1334 		h[i] = XtNewString(((XmTabsWidget)self)->xmTabs.labels[i]);
   1335 	    ((XmTabsWidget)self)->xmTabs.labels = h;
   1336 	}
   1337 	if (((XmTabsWidget)old)->xmTabs.labels) {
   1338 	    for (i = ((XmTabsWidget)old)->xmTabs.lefttabs + ((XmTabsWidget)old)->xmTabs.righttabs; i >= 0; i--)
   1339 		XtFree(((XmTabsWidget)old)->xmTabs.labels[i]);
   1340 	    XtFree((XtPointer) ((XmTabsWidget)old)->xmTabs.labels);
   1341 	}
   1342 	resize_labels = TRUE;
   1343     }
   1344     if (((XmTabsWidget)self)->xmTabs.tabWidthPercentage < 0 || ((XmTabsWidget)self)->xmTabs.tabWidthPercentage > 100) {
   1345 	XtAppWarning(XtWidgetToApplicationContext(self),
   1346 		     "tabWidthPercentage out of range; reset to 50");
   1347 	((XmTabsWidget)self)->xmTabs.tabWidthPercentage = 50;
   1348     }
   1349     if (((XmTabsWidget)old)->xmTabs.tabWidthPercentage != ((XmTabsWidget)self)->xmTabs.tabWidthPercentage)
   1350 	resize_labels = TRUE;
   1351     if (((XmTabsWidget)self)->xmTabs.lefttabs != ((XmTabsWidget)old)->xmTabs.lefttabs || ((XmTabsWidget)self)->xmTabs.righttabs != ((XmTabsWidget)old)->xmTabs.righttabs)
   1352 	redraw = TRUE;
   1353     if (resize_labels) {
   1354 	compute_tabsizes(self);
   1355 	redraw = TRUE;
   1356     }
   1357     return redraw;
   1358 }
   1359 /*ARGSUSED*/
   1360 #if NeedFunctionPrototypes
   1361 static void realize(Widget self,XtValueMask * mask,XSetWindowAttributes * attributes)
   1362 #else
   1363 static void realize(self,mask,attributes)Widget self;XtValueMask * mask;XSetWindowAttributes * attributes;
   1364 #endif
   1365 {
   1366     *mask |= CWBitGravity;
   1367     attributes->bit_gravity = ForgetGravity;
   1368     xmManagerClassRec.core_class.realize(self, mask, attributes);
   1369     set_shape(self);
   1370 }
   1371 /*ARGSUSED*/
   1372 #if NeedFunctionPrototypes
   1373 static void resize(Widget self)
   1374 #else
   1375 static void resize(self)Widget self;
   1376 #endif
   1377 {
   1378     if (XtIsRealized(self))
   1379 	XClearArea(XtDisplay(self), XtWindow(self), 0, 0, 0, 0, True);
   1380     compute_tabsizes(self);
   1381     set_shape(self);
   1382     if ( xmManagerClassRec.core_class.resize ) xmManagerClassRec.core_class.resize(self);
   1383 }
   1384 /*ARGSUSED*/
   1385 #if NeedFunctionPrototypes
   1386 static void expose(Widget self,XEvent * event,Region  region)
   1387 #else
   1388 static void expose(self,event,region)Widget self;XEvent * event;Region  region;
   1389 #endif
   1390 {
   1391     int i;
   1392 
   1393     if (! XtIsRealized(self)) return;
   1394 
   1395     switch (((XmTabsWidget)self)->xmTabs.orientation) {
   1396     case XfwfUpTabs:
   1397     case XfwfDownTabs:
   1398 	for (i = 0; i < ((XmTabsWidget)self)->xmTabs.lefttabs; i++)
   1399 	    draw_hor_tab(self, region, i);
   1400 	for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i > ((XmTabsWidget)self)->xmTabs.lefttabs; i--)
   1401 	    draw_hor_tab(self, region, i);
   1402 	draw_hor_tab(self, region, ((XmTabsWidget)self)->xmTabs.lefttabs);
   1403 	break;
   1404     case XfwfLeftTabs:
   1405     case XfwfRightTabs:
   1406 	for (i = 0; i < ((XmTabsWidget)self)->xmTabs.lefttabs; i++)
   1407 	    draw_ver_tab(self, region, i);
   1408 	for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i > ((XmTabsWidget)self)->xmTabs.lefttabs; i--)
   1409 	    draw_ver_tab(self, region, i);
   1410 	draw_ver_tab(self, region, ((XmTabsWidget)self)->xmTabs.lefttabs);
   1411 	break;
   1412     }
   1413     /* Focus highlight? */
   1414 }
   1415 /*ARGSUSED*/
   1416 #if NeedFunctionPrototypes
   1417 static void border_highlight(void)
   1418 #else
   1419 static void border_highlight(void)
   1420 #endif
   1421 {
   1422 }
   1423 /*ARGSUSED*/
   1424 #if NeedFunctionPrototypes
   1425 static void border_unhighlight(void)
   1426 #else
   1427 static void border_unhighlight(void)
   1428 #endif
   1429 {
   1430 }
   1431 /*ARGSUSED*/
   1432 #if NeedFunctionPrototypes
   1433 static void destroy(Widget self)
   1434 #else
   1435 static void destroy(self)Widget self;
   1436 #endif
   1437 {
   1438     int i;
   1439 
   1440     if (((XmTabsWidget)self)->xmTabs.labels) {
   1441 	for (i = ((XmTabsWidget)self)->xmTabs.lefttabs + ((XmTabsWidget)self)->xmTabs.righttabs; i >= 0; i--)
   1442 	    XtFree(((XmTabsWidget)self)->xmTabs.labels[i]);
   1443 	XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.labels);
   1444     }
   1445     if (((XmTabsWidget)self)->xmTabs.offsets)
   1446 	XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.offsets);
   1447     if (((XmTabsWidget)self)->xmTabs.tabwidths)
   1448 	XtFree((XtPointer) ((XmTabsWidget)self)->xmTabs.tabwidths);
   1449 }
   1450 
   1451 
   1452 /* ***********************************************************************
   1454  * Routines for drawing rotated text
   1455  * ***********************************************************************
   1456  */
   1457 
   1458 /* xvertext 5.0, Copyright (c) 1993 Alan Richardson (mppa3 (at) uk.ac.sussex.syma)
   1459  *
   1460  * Permission to use, copy, modify, and distribute this software and its
   1461  * documentation for any purpose and without fee is hereby granted, provided
   1462  * that the above copyright notice appear in all copies and that both the
   1463  * copyright notice and this permission notice appear in supporting
   1464  * documentation.  All work developed as a consequence of the use of
   1465  * this program should duly acknowledge such use. No representations are
   1466  * made about the suitability of this software for any purpose.  It is
   1467  * provided "as is" without express or implied warranty.
   1468  *
   1469  * 8 Jun '95: [Bert Bos] added GCClipXOrigin|GCClipYOrigin|GCClipMask
   1470  * when calling XCopyGC()
   1471  */
   1472 
   1473 /* ********************************************************************** */
   1474 
   1475 
   1476 /* BETTER: xvertext now does rotation at any angle!!
   1477  *
   1478  * BEWARE: function arguments have CHANGED since version 2.0!!
   1479  */
   1480 
   1481 /* ********************************************************************** */
   1482 
   1483 
   1484 
   1485 /* ---------------------------------------------------------------------- */
   1486 
   1487 
   1488 /* Make sure cache size is set */
   1489 
   1490 #ifndef CACHE_SIZE_LIMIT
   1491 #define CACHE_SIZE_LIMIT 0
   1492 #endif /*CACHE_SIZE_LIMIT */
   1493 
   1494 /* Make sure a cache method is specified */
   1495 
   1496 #ifndef CACHE_XIMAGES
   1497 #ifndef CACHE_BITMAPS
   1498 #define CACHE_BITMAPS
   1499 #endif /*CACHE_BITMAPS*/
   1500 #endif /*CACHE_XIMAGES*/
   1501 
   1502 
   1503 /* ---------------------------------------------------------------------- */
   1504 
   1505 
   1506 /* Debugging macros */
   1507 
   1508 #ifdef DEBUG
   1509 static int debug=1;
   1510 #else
   1511 static int debug=0;
   1512 #endif /*DEBUG*/
   1513 
   1514 #define DEBUG_PRINT1(a) if (debug) printf (a)
   1515 #define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
   1516 #define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
   1517 #define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
   1518 #define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)
   1519 
   1520 
   1521 /* ---------------------------------------------------------------------- */
   1522 
   1523 
   1524 #ifndef M_PI
   1525 #define M_PI 3.14159265358979323846
   1526 #endif
   1527 
   1528 
   1529 /* ---------------------------------------------------------------------- */
   1530 
   1531 
   1532 /* A structure holding everything needed for a rotated string */
   1533 
   1534 typedef struct rotated_text_item_template {
   1535     Pixmap bitmap;
   1536     XImage *ximage;
   1537 
   1538     char *text;
   1539     char *font_name;
   1540     Font fid;
   1541     float angle;
   1542     int align;
   1543     float magnify;
   1544 
   1545     int cols_in;
   1546     int rows_in;
   1547     int cols_out;
   1548     int rows_out;
   1549 
   1550     int nl;
   1551     int max_width;
   1552     float *corners_x;
   1553     float *corners_y;
   1554 
   1555     long int size;
   1556     int cached;
   1557 
   1558     struct rotated_text_item_template *next;
   1559 } RotatedTextItem;
   1560 
   1561 RotatedTextItem *first_text_item=NULL;
   1562 
   1563 
   1564 /* ---------------------------------------------------------------------- */
   1565 
   1566 
   1567 /* A structure holding current magnification and bounding box padding */
   1568 
   1569 static struct style_template {
   1570     float magnify;
   1571     int bbx_pad;
   1572 } style={
   1573     1.,
   1574     0
   1575     };
   1576 
   1577 
   1578 /* ---------------------------------------------------------------------- */
   1579 
   1580 
   1581 static char            *my_strdup(char *str);
   1582 static char            *my_strtok(char *str1, char *str2);
   1583 
   1584 static float                   XRotVersion(char *str, int n);
   1585 static void                    XRotSetMagnification(float m);
   1586 static void                    XRotSetBoundingBoxPad(int p);
   1587 static int                     XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str);
   1588 static int                     XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str);
   1589 static int                     XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align);
   1590 static int                     XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align);
   1591 static XPoint                 *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align);
   1592 
   1593 static XImage          *MakeXImage(Display *dpy, int w, int h);
   1594 static int              XRotPaintAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg);
   1595 static int              XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg);
   1596 static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, float angle, char *text, int align);
   1597 static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, float angle, char *text, int align);
   1598 static void             XRotAddToLinkedList(Display *dpy, RotatedTextItem *item);
   1599 static void             XRotFreeTextItem(Display *dpy, RotatedTextItem *item);
   1600 static XImage          *XRotMagnifyImage(Display *dpy, XImage *ximage);
   1601 
   1602 
   1603 /* ---------------------------------------------------------------------- */
   1604 
   1605 
   1606 /**************************************************************************/
   1607 /* Routine to mimic `strdup()' (some machines don't have it)              */
   1608 /**************************************************************************/
   1609 
   1610 static char *my_strdup(char *str)
   1611 {
   1612     char *s;
   1613 
   1614     if(str==NULL)
   1615 	return NULL;
   1616 
   1617     s=(char *)malloc((unsigned)(strlen(str)+1));
   1618     if(s!=NULL)
   1619 	strcpy(s, str);
   1620 
   1621     return s;
   1622 }
   1623 
   1624 
   1625 /* ---------------------------------------------------------------------- */
   1626 
   1627 
   1628 /**************************************************************************/
   1629 /* Routine to replace `strtok' : this one returns a zero length string if */
   1630 /* it encounters two consecutive delimiters                               */
   1631 /**************************************************************************/
   1632 
   1633 static char *my_strtok(char *str1, char *str2)
   1634 {
   1635     char *ret;
   1636     int i, j, stop;
   1637     static int start, len;
   1638     static char *stext;
   1639 
   1640     if(str2==NULL)
   1641 	return NULL;
   1642 
   1643     /* initialise if str1 not NULL */
   1644     if(str1!=NULL) {
   1645 	start=0;
   1646 	stext=str1;
   1647 	len=strlen(str1);
   1648     }
   1649 
   1650     /* run out of tokens ? */
   1651     if(start>=len)
   1652 	return NULL;
   1653 
   1654     /* loop through characters */
   1655     for(i=start; i<len; i++) {
   1656 	/* loop through delimiters */
   1657 	stop=0;
   1658 	for(j=0; j<strlen(str2); j++)
   1659 	    if(stext[i]==str2[j])
   1660 		stop=1;
   1661 
   1662 	if(stop)
   1663 	    break;
   1664     }
   1665 
   1666     stext[i]='\0';
   1667 
   1668     ret=stext+start;
   1669 
   1670     start=i+1;
   1671 
   1672     return ret;
   1673 }
   1674 
   1675 
   1676 /* ---------------------------------------------------------------------- */
   1677 
   1678 
   1679 /**************************************************************************/
   1680 /* Return version/copyright information                                   */
   1681 /**************************************************************************/
   1682 static
   1683 float XRotVersion(char *str, int n)
   1684 {
   1685     if(str!=NULL)
   1686 	strncpy(str, XV_COPYRIGHT, n);
   1687     return XV_VERSION;
   1688 }
   1689 
   1690 
   1691 /* ---------------------------------------------------------------------- */
   1692 
   1693 
   1694 /**************************************************************************/
   1695 /* Set the font magnification factor for all subsequent operations        */
   1696 /**************************************************************************/
   1697 static
   1698 void XRotSetMagnification(float m)
   1699 {
   1700     if(m>0.)
   1701 	style.magnify=m;
   1702 }
   1703 
   1704 
   1705 /* ---------------------------------------------------------------------- */
   1706 
   1707 
   1708 /**************************************************************************/
   1709 /* Set the padding used when calculating bounding boxes                   */
   1710 /**************************************************************************/
   1711 static
   1712 void XRotSetBoundingBoxPad(int p)
   1713 {
   1714     if(p>=0)
   1715 	style.bbx_pad=p;
   1716 }
   1717 
   1718 
   1719 /* ---------------------------------------------------------------------- */
   1720 
   1721 
   1722 /**************************************************************************/
   1723 /*  Create an XImage structure and allocate memory for it                 */
   1724 /**************************************************************************/
   1725 
   1726 static XImage *MakeXImage(Display *dpy, int w, int h)
   1727 {
   1728     XImage *I;
   1729     char *data;
   1730 
   1731     /* reserve memory for image */
   1732     data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
   1733     if(data==NULL)
   1734 	return NULL;
   1735 
   1736     /* create the XImage */
   1737     I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap,
   1738                    0, data, w, h, 8, 0);
   1739     if(I==NULL)
   1740 	return NULL;
   1741 
   1742     I->byte_order=I->bitmap_bit_order=MSBFirst;
   1743     return I;
   1744 }
   1745 
   1746 
   1747 /* ---------------------------------------------------------------------- */
   1748 
   1749 
   1750 /**************************************************************************/
   1751 /*  A front end to XRotPaintAlignedString:                                */
   1752 /*      -no alignment, no background                                      */
   1753 /**************************************************************************/
   1754 static
   1755 int XRotDrawString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str)
   1756 {
   1757     return (XRotPaintAlignedString(dpy, font, angle, drawable, gc,
   1758 				   x, y, str, NONE, 0));
   1759 }
   1760 
   1761 
   1762 /* ---------------------------------------------------------------------- */
   1763 
   1764 
   1765 /**************************************************************************/
   1766 /*  A front end to XRotPaintAlignedString:                                */
   1767 /*      -no alignment, paints background                                  */
   1768 /**************************************************************************/
   1769 static
   1770 int XRotDrawImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *str)
   1771 {
   1772     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
   1773 				  x, y, str, NONE, 1));
   1774 }
   1775 
   1776 
   1777 /* ---------------------------------------------------------------------- */
   1778 
   1779 
   1780 /**************************************************************************/
   1781 /*  A front end to XRotPaintAlignedString:                                */
   1782 /*      -does alignment, no background                                    */
   1783 /**************************************************************************/
   1784 static
   1785 int XRotDrawAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align)
   1786 {
   1787     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
   1788 				  x, y, text, align, 0));
   1789 }
   1790 
   1791 
   1792 /* ---------------------------------------------------------------------- */
   1793 
   1794 
   1795 /**************************************************************************/
   1796 /*  A front end to XRotPaintAlignedString:                                */
   1797 /*      -does alignment, paints background                                */
   1798 /**************************************************************************/
   1799 static
   1800 int XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align)
   1801 {
   1802     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
   1803 				  x, y, text, align, 1));
   1804 }
   1805 
   1806 
   1807 /* ---------------------------------------------------------------------- */
   1808 
   1809 
   1810 /**************************************************************************/
   1811 /*  Aligns and paints a rotated string                                    */
   1812 /**************************************************************************/
   1813 
   1814 static int XRotPaintAlignedString(Display *dpy, XFontStruct *font, float angle, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg)
   1815 {
   1816     int i;
   1817     GC my_gc;
   1818     int xp, yp;
   1819     float hot_x, hot_y;
   1820     float hot_xp, hot_yp;
   1821     float sin_angle, cos_angle;
   1822     RotatedTextItem *item;
   1823     Pixmap bitmap_to_paint;
   1824 
   1825     /* return early for NULL/empty strings */
   1826     if(text==NULL)
   1827         return 0;
   1828 
   1829     if(strlen(text)==0)
   1830 	return 0;
   1831 
   1832     /* manipulate angle to 0<=angle<360 degrees */
   1833     while(angle<0)
   1834         angle+=360;
   1835 
   1836     while(angle>=360)
   1837         angle-=360;
   1838 
   1839     angle*=M_PI/180;
   1840 
   1841     /* horizontal text made easy */
   1842     if(angle==0. && style.magnify==1.)
   1843 	return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
   1844 					text, align, bg));
   1845 
   1846     /* get a rotated bitmap */
   1847     item=XRotRetrieveFromCache(dpy, font, angle, text, align);
   1848     if(item==NULL)
   1849 	return NULL;
   1850 
   1851     /* this gc has similar properties to the user's gc */
   1852     my_gc=XCreateGC(dpy, drawable, NULL, 0);
   1853     XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask
   1854 	    |GCClipXOrigin|GCClipYOrigin|GCClipMask, my_gc);
   1855 
   1856     /* alignment : which point (hot_x, hot_y) relative to bitmap centre
   1857        coincides with user's specified point? */
   1858 
   1859     /* y position */
   1860     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
   1861         hot_y=(float)item->rows_in/2*style.magnify;
   1862     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
   1863 	hot_y=0;
   1864     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
   1865 	hot_y = -(float)item->rows_in/2*style.magnify;
   1866     else
   1867 	hot_y = -((float)item->rows_in/2-(float)font->descent)*style.magnify;
   1868 
   1869     /* x position */
   1870     if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
   1871 	hot_x = -(float)item->max_width/2*style.magnify;
   1872     else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
   1873 	hot_x=0;
   1874     else
   1875         hot_x=(float)item->max_width/2*style.magnify;
   1876 
   1877     /* pre-calculate sin and cos */
   1878     sin_angle=sin(angle);
   1879     cos_angle=cos(angle);
   1880 
   1881     /* rotate hot_x and hot_y around bitmap centre */
   1882     hot_xp= hot_x*cos_angle - hot_y*sin_angle;
   1883     hot_yp= hot_x*sin_angle + hot_y*cos_angle;
   1884 
   1885     /* text background will be drawn using XFillPolygon */
   1886     if(bg) {
   1887 	GC depth_one_gc;
   1888 	XPoint *xpoints;
   1889 	Pixmap empty_stipple;
   1890 
   1891 	/* reserve space for XPoints */
   1892 	xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
   1893 	if(!xpoints)
   1894 	    return 1;
   1895 
   1896 	/* rotate corner positions */
   1897 	for(i=0; i<4*item->nl; i++) {
   1898 	    xpoints[i].x=(float)x + ( (item->corners_x[i]-hot_x)*cos_angle +
   1899 				      (item->corners_y[i]+hot_y)*sin_angle);
   1900 	    xpoints[i].y=(float)y + (-(item->corners_x[i]-hot_x)*sin_angle +
   1901 				      (item->corners_y[i]+hot_y)*cos_angle);
   1902 	}
   1903 
   1904 	/* we want to swap foreground and background colors here;
   1905 	   XGetGCValues() is only available in R4+ */
   1906 
   1907 	empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);
   1908 
   1909 	depth_one_gc=XCreateGC(dpy, empty_stipple, NULL, 0);
   1910 	XSetForeground(dpy, depth_one_gc, 0);
   1911 	XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);
   1912 
   1913 	XSetStipple(dpy, my_gc, empty_stipple);
   1914 	XSetFillStyle(dpy, my_gc, FillOpaqueStippled);
   1915 
   1916 	XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
   1917 		     CoordModeOrigin);
   1918 
   1919 	/* free our resources */
   1920 	free((char *)xpoints);
   1921 	XFreeGC(dpy, depth_one_gc);
   1922 	XFreePixmap(dpy, empty_stipple);
   1923     }
   1924 
   1925     /* where should top left corner of bitmap go ? */
   1926     xp=(float)x-((float)item->cols_out/2 +hot_xp);
   1927     yp=(float)y-((float)item->rows_out/2 -hot_yp);
   1928 
   1929     /* by default we draw the rotated bitmap, solid */
   1930     bitmap_to_paint=item->bitmap;
   1931 
   1932     /* handle user stippling */
   1933 #ifndef X11R3
   1934     {
   1935 	GC depth_one_gc;
   1936 	XGCValues values;
   1937 	Pixmap new_bitmap, inverse;
   1938 
   1939 	/* try and get some GC properties */
   1940 	if(XGetGCValues(dpy, gc,
   1941 			GCStipple|GCFillStyle|GCForeground|GCBackground|
   1942 			GCTileStipXOrigin|GCTileStipYOrigin,
   1943 			&values)) {
   1944 
   1945 	    /* only do this if stippling requested */
   1946 	    if((values.fill_style==FillStippled ||
   1947 		values.fill_style==FillOpaqueStippled) && !bg) {
   1948 
   1949 		/* opaque stipple: draw rotated text in background colour */
   1950 		if(values.fill_style==FillOpaqueStippled) {
   1951 		    XSetForeground(dpy, my_gc, values.background);
   1952 		    XSetFillStyle(dpy, my_gc, FillStippled);
   1953 		    XSetStipple(dpy, my_gc, item->bitmap);
   1954 		    XSetTSOrigin(dpy, my_gc, xp, yp);
   1955 		    XFillRectangle(dpy, drawable, my_gc, xp, yp,
   1956 				   item->cols_out, item->rows_out);
   1957 		    XSetForeground(dpy, my_gc, values.foreground);
   1958 		}
   1959 
   1960 		/* this will merge the rotated text and the user's stipple */
   1961 		new_bitmap=XCreatePixmap(dpy, drawable,
   1962 					 item->cols_out, item->rows_out, 1);
   1963 
   1964 		/* create a GC */
   1965 		depth_one_gc=XCreateGC(dpy, new_bitmap, NULL, 0);
   1966 		XSetForeground(dpy, depth_one_gc, 1);
   1967 		XSetBackground(dpy, depth_one_gc, 0);
   1968 
   1969 		/* set the relative stipple origin */
   1970 		XSetTSOrigin(dpy, depth_one_gc,
   1971 			     values.ts_x_origin-xp, values.ts_y_origin-yp);
   1972 
   1973 		/* fill the whole bitmap with the user's stipple */
   1974 		XSetStipple(dpy, depth_one_gc, values.stipple);
   1975 		XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
   1976 		XFillRectangle(dpy, new_bitmap, depth_one_gc,
   1977 			       0, 0, item->cols_out, item->rows_out);
   1978 
   1979 		/* set stipple origin back to normal */
   1980 		XSetTSOrigin(dpy, depth_one_gc, 0, 0);
   1981 
   1982 		/* this will contain an inverse copy of the rotated text */
   1983 		inverse=XCreatePixmap(dpy, drawable,
   1984 				      item->cols_out, item->rows_out, 1);
   1985 
   1986 		/* invert text */
   1987 		XSetFillStyle(dpy, depth_one_gc, FillSolid);
   1988 		XSetFunction(dpy, depth_one_gc, GXcopyInverted);
   1989 		XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
   1990 			  0, 0, item->cols_out, item->rows_out, 0, 0);
   1991 
   1992 		/* now delete user's stipple everywhere EXCEPT on text */
   1993                 XSetForeground(dpy, depth_one_gc, 0);
   1994                 XSetBackground(dpy, depth_one_gc, 1);
   1995 		XSetStipple(dpy, depth_one_gc, inverse);
   1996 		XSetFillStyle(dpy, depth_one_gc, FillStippled);
   1997 		XSetFunction(dpy, depth_one_gc, GXcopy);
   1998 		XFillRectangle(dpy, new_bitmap, depth_one_gc,
   1999                                0, 0, item->cols_out, item->rows_out);
   2000 
   2001 		/* free resources */
   2002 		XFreePixmap(dpy, inverse);
   2003 		XFreeGC(dpy, depth_one_gc);
   2004 
   2005 		/* this is the new bitmap */
   2006 		bitmap_to_paint=new_bitmap;
   2007 	    }
   2008 	}
   2009     }
   2010 #endif /*X11R3*/
   2011 
   2012     /* paint text using stipple technique */
   2013     XSetFillStyle(dpy, my_gc, FillStippled);
   2014     XSetStipple(dpy, my_gc, bitmap_to_paint);
   2015     XSetTSOrigin(dpy, my_gc, xp, yp);
   2016     XFillRectangle(dpy, drawable, my_gc, xp, yp,
   2017 		   item->cols_out, item->rows_out);
   2018 
   2019     /* free our resources */
   2020     XFreeGC(dpy, my_gc);
   2021 
   2022     /* stippled bitmap no longer needed */
   2023     if(bitmap_to_paint!=item->bitmap)
   2024 	XFreePixmap(dpy, bitmap_to_paint);
   2025 
   2026 #ifdef CACHE_XIMAGES
   2027     XFreePixmap(dpy, item->bitmap);
   2028 #endif /*CACHE_XIMAGES*/
   2029 
   2030     /* if item isn't cached, destroy it completely */
   2031     if(!item->cached)
   2032 	XRotFreeTextItem(dpy,item);
   2033 
   2034     /* we got to the end OK! */
   2035     return 0;
   2036 }
   2037 
   2038 
   2039 /* ---------------------------------------------------------------------- */
   2040 
   2041 
   2042 /**************************************************************************/
   2043 /*  Draw a horizontal string in a quick fashion                           */
   2044 /**************************************************************************/
   2045 
   2046 static int XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, char *text, int align, int bg)
   2047 {
   2048     GC my_gc;
   2049     int nl=1, i;
   2050     int height;
   2051     int xp, yp;
   2052     char *str1, *str2, *str3;
   2053     char *str2_a="\0", *str2_b="\n\0";
   2054     int dir, asc, desc;
   2055     XCharStruct overall;
   2056 
   2057     DEBUG_PRINT1("**\nHorizontal text.\n");
   2058 
   2059     /* this gc has similar properties to the user's gc (including stipple) */
   2060     my_gc=XCreateGC(dpy, drawable, NULL, 0);
   2061     XCopyGC(dpy, gc,
   2062 	    GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
   2063 	    GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask|
   2064 	    GCClipXOrigin|GCClipYOrigin|GCClipMask, my_gc);
   2065     XSetFont(dpy, my_gc, font->fid);
   2066 
   2067     /* count number of sections in string */
   2068     if(align!=NONE)
   2069 	for(i=0; i<strlen(text)-1; i++)
   2070 	    if(text[i]=='\n')
   2071 		nl++;
   2072 
   2073     /* ignore newline characters if not doing alignment */
   2074     if(align==NONE)
   2075 	str2=str2_a;
   2076     else
   2077 	str2=str2_b;
   2078 
   2079     /* overall font height */
   2080     height=font->ascent+font->descent;
   2081 
   2082     /* y position */
   2083     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
   2084 	yp=y+font->ascent;
   2085     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
   2086 	yp=y-nl*height/2+font->ascent;
   2087     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
   2088 	yp=y-nl*height+font->ascent;
   2089     else
   2090 	yp=y;
   2091 
   2092     str1=my_strdup(text);
   2093     if(str1==NULL)
   2094 	return 1;
   2095 
   2096     str3=my_strtok(str1, str2);
   2097 
   2098     /* loop through each section in the string */
   2099     do {
   2100         XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2101                      &overall);
   2102 
   2103 	/* where to draw section in x ? */
   2104 	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
   2105 	    xp=x;
   2106 	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
   2107 	    xp=x-overall.rbearing/2;
   2108 	else
   2109 	    xp=x-overall.rbearing;
   2110 
   2111 	/* draw string onto bitmap */
   2112 	if(!bg)
   2113 	    XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
   2114 	else
   2115 	    XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
   2116 
   2117 	/* move to next line */
   2118 	yp+=height;
   2119 
   2120 	str3=my_strtok((char *)NULL, str2);
   2121     }
   2122     while(str3!=NULL);
   2123 
   2124     free(str1);
   2125     XFreeGC(dpy, my_gc);
   2126 
   2127     return 0;
   2128 }
   2129 
   2130 
   2131 /* ---------------------------------------------------------------------- */
   2132 
   2133 
   2134 /**************************************************************************/
   2135 /*   Query cache for a match with this font/text/angle/alignment          */
   2136 /*       request, otherwise arrange for its creation                      */
   2137 /**************************************************************************/
   2138 
   2139 static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, float angle, char *text, int align)
   2140 {
   2141     Font fid;
   2142     char *font_name=NULL;
   2143     unsigned long name_value;
   2144     RotatedTextItem *item=NULL;
   2145     RotatedTextItem *i1=first_text_item;
   2146 
   2147     /* get font name, if it exists */
   2148     if(XGetFontProperty(font, XA_FONT, &name_value)) {
   2149 	DEBUG_PRINT1("got font name OK\n");
   2150 	font_name=XGetAtomName(dpy, name_value);
   2151 	fid=0;
   2152     }
   2153 #ifdef CACHE_FID
   2154     /* otherwise rely (unreliably?) on font ID */
   2155     else {
   2156 	DEBUG_PRINT1("can't get fontname, caching FID\n");
   2157 	font_name=NULL;
   2158 	fid=font->fid;
   2159     }
   2160 #else
   2161     /* not allowed to cache font ID's */
   2162     else {
   2163 	DEBUG_PRINT1("can't get fontname, can't cache\n");
   2164 	font_name=NULL;
   2165 	fid=0;
   2166     }
   2167 #endif /*CACHE_FID*/
   2168 
   2169     /* look for a match in cache */
   2170 
   2171     /* matching formula:
   2172        identical text;
   2173        identical fontname (if defined, font ID's if not);
   2174        angles close enough (<0.00001 here, could be smaller);
   2175        HORIZONTAL alignment matches, OR it's a one line string;
   2176        magnifications the same */
   2177 
   2178     while(i1 && !item) {
   2179 	/* match everything EXCEPT fontname/ID */
   2180 	if(strcmp(text, i1->text)==0 &&
   2181 	   fabs(angle-i1->angle)<0.00001 &&
   2182 	   style.magnify==i1->magnify &&
   2183 	   (i1->nl==1 ||
   2184 	    ((align==0)?9:(align-1))%3==
   2185 	      ((i1->align==0)?9:(i1->align-1))%3)) {
   2186 
   2187 	    /* now match fontname/ID */
   2188 	    if(font_name!=NULL && i1->font_name!=NULL) {
   2189 		if(strcmp(font_name, i1->font_name)==0) {
   2190 		    item=i1;
   2191 		    DEBUG_PRINT1("Matched against font names\n");
   2192 		}
   2193 		else
   2194 		    i1=i1->next;
   2195 	    }
   2196 #ifdef CACHE_FID
   2197 	    else if(font_name==NULL && i1->font_name==NULL) {
   2198 		if(fid==i1->fid) {
   2199 		    item=i1;
   2200 		    DEBUG_PRINT1("Matched against FID's\n");
   2201                 }
   2202 		else
   2203                     i1=i1->next;
   2204 	    }
   2205 #endif /*CACHE_FID*/
   2206 	    else
   2207 		i1=i1->next;
   2208 	}
   2209 	else
   2210 	    i1=i1->next;
   2211     }
   2212 
   2213     if(item)
   2214 	DEBUG_PRINT1("**\nFound target in cache.\n");
   2215     if(!item)
   2216 	DEBUG_PRINT1("**\nNo match in cache.\n");
   2217 
   2218     /* no match */
   2219     if(!item) {
   2220 	/* create new item */
   2221 	item=XRotCreateTextItem(dpy, font, angle, text, align);
   2222 	if(!item)
   2223 	    return NULL;
   2224 
   2225 	/* record what it shows */
   2226 	item->text=my_strdup(text);
   2227 
   2228 	/* fontname or ID */
   2229 	if(font_name!=NULL) {
   2230 	    item->font_name=my_strdup(font_name);
   2231 	    item->fid=0;
   2232 	}
   2233 	else {
   2234 	    item->font_name=NULL;
   2235 	    item->fid=fid;
   2236 	}
   2237 
   2238 	item->angle=angle;
   2239 	item->align=align;
   2240 	item->magnify=style.magnify;
   2241 
   2242 	/* cache it */
   2243 	XRotAddToLinkedList(dpy, item);
   2244     }
   2245 
   2246     if(font_name)
   2247 	XFree(font_name);
   2248 
   2249     /* if XImage is cached, need to recreate the bitmap */
   2250 
   2251 #ifdef CACHE_XIMAGES
   2252     {
   2253 	GC depth_one_gc;
   2254 
   2255 	/* create bitmap to hold rotated text */
   2256 	item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
   2257 				   item->cols_out, item->rows_out, 1);
   2258 
   2259 	/* depth one gc */
   2260 	depth_one_gc=XCreateGC(dpy, item->bitmap, NULL, 0);
   2261 	XSetBackground(dpy, depth_one_gc, 0);
   2262 	XSetForeground(dpy, depth_one_gc, 1);
   2263 
   2264 	/* make the text bitmap from XImage */
   2265 	XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
   2266 		  item->cols_out, item->rows_out);
   2267 
   2268 	XFreeGC(dpy, depth_one_gc);
   2269     }
   2270 #endif /*CACHE_XIMAGES*/
   2271 
   2272     return item;
   2273 }
   2274 
   2275 
   2276 /* ---------------------------------------------------------------------- */
   2277 
   2278 
   2279 /**************************************************************************/
   2280 /*  Create a rotated text item                                            */
   2281 /**************************************************************************/
   2282 
   2283 static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, float angle, char *text, int align)
   2284 {
   2285     RotatedTextItem *item=NULL;
   2286     Pixmap canvas;
   2287     GC font_gc;
   2288     XImage *I_in;
   2289     register int i, j;
   2290     char *str1, *str2, *str3;
   2291     char *str2_a="\0", *str2_b="\n\0";
   2292     int height;
   2293     int byte_w_in, byte_w_out;
   2294     int xp, yp;
   2295     float sin_angle, cos_angle;
   2296     int it, jt;
   2297     float di, dj;
   2298     int ic=0;
   2299     float xl, xr, xinc;
   2300     int byte_out;
   2301     int dir, asc, desc;
   2302     XCharStruct overall;
   2303     int old_cols_in=0, old_rows_in=0;
   2304 
   2305     /* allocate memory */
   2306     item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
   2307     if(!item)
   2308 	return NULL;
   2309 
   2310     /* count number of sections in string */
   2311     item->nl=1;
   2312     if(align!=NONE)
   2313 	for(i=0; i<strlen(text)-1; i++)
   2314 	    if(text[i]=='\n')
   2315 		item->nl++;
   2316 
   2317     /* ignore newline characters if not doing alignment */
   2318     if(align==NONE)
   2319 	str2=str2_a;
   2320     else
   2321 	str2=str2_b;
   2322 
   2323     /* find width of longest section */
   2324     str1=my_strdup(text);
   2325     if(str1==NULL)
   2326 	return NULL;
   2327 
   2328     str3=my_strtok(str1, str2);
   2329 
   2330     XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2331 		 &overall);
   2332 
   2333     item->max_width=overall.rbearing;
   2334 
   2335     /* loop through each section */
   2336     do {
   2337 	str3=my_strtok((char *)NULL, str2);
   2338 
   2339 	if(str3!=NULL) {
   2340 	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2341 			 &overall);
   2342 
   2343 	    if(overall.rbearing>item->max_width)
   2344 		item->max_width=overall.rbearing;
   2345 	}
   2346     }
   2347     while(str3!=NULL);
   2348 
   2349     free(str1);
   2350 
   2351     /* overall font height */
   2352     height=font->ascent+font->descent;
   2353 
   2354     /* dimensions horizontal text will have */
   2355     item->cols_in=item->max_width;
   2356     item->rows_in=item->nl*height;
   2357 
   2358     /* bitmap for drawing on */
   2359     canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
   2360 			 item->cols_in, item->rows_in, 1);
   2361 
   2362     /* create a GC for the bitmap */
   2363     font_gc=XCreateGC(dpy, canvas, NULL, 0);
   2364     XSetBackground(dpy, font_gc, 0);
   2365     XSetFont(dpy, font_gc, font->fid);
   2366 
   2367     /* make sure the bitmap is blank */
   2368     XSetForeground(dpy, font_gc, 0);
   2369     XFillRectangle(dpy, canvas, font_gc, 0, 0,
   2370 		   item->cols_in+1, item->rows_in+1);
   2371     XSetForeground(dpy, font_gc, 1);
   2372 
   2373     /* pre-calculate sin and cos */
   2374     sin_angle=sin(angle);
   2375     cos_angle=cos(angle);
   2376 
   2377     /* text background will be drawn using XFillPolygon */
   2378     item->corners_x=
   2379 	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
   2380     if(!item->corners_x)
   2381 	return NULL;
   2382 
   2383     item->corners_y=
   2384 	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
   2385     if(!item->corners_y)
   2386 	return NULL;
   2387 
   2388     /* draw text horizontally */
   2389 
   2390     /* start at top of bitmap */
   2391     yp=font->ascent;
   2392 
   2393     str1=my_strdup(text);
   2394     if(str1==NULL)
   2395 	return NULL;
   2396 
   2397     str3=my_strtok(str1, str2);
   2398 
   2399     /* loop through each section in the string */
   2400     do {
   2401 	XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2402 		&overall);
   2403 
   2404 	/* where to draw section in x ? */
   2405 	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
   2406 	    xp=0;
   2407 	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
   2408 	    xp=(item->max_width-overall.rbearing)/2;
   2409 	else
   2410             xp=item->max_width-overall.rbearing;
   2411 
   2412 	/* draw string onto bitmap */
   2413 	XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3));
   2414 
   2415 	/* keep a note of corner positions of this string */
   2416 	item->corners_x[ic]=((float)xp-(float)item->cols_in/2)*style.magnify;
   2417 	item->corners_y[ic]=((float)(yp-font->ascent)-(float)item->rows_in/2)
   2418 	    *style.magnify;
   2419 	item->corners_x[ic+1]=item->corners_x[ic];
   2420 	item->corners_y[ic+1]=item->corners_y[ic]+(float)height*style.magnify;
   2421 	item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
   2422 	    (float)overall.rbearing*style.magnify;
   2423 	item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
   2424 	item->corners_x[item->nl*4-2-ic]=
   2425 	    item->corners_x[item->nl*4-1-ic];
   2426 	item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];
   2427 
   2428 	ic+=2;
   2429 
   2430 	/* move to next line */
   2431 	yp+=height;
   2432 
   2433 	str3=my_strtok((char *)NULL, str2);
   2434     }
   2435     while(str3!=NULL);
   2436 
   2437     free(str1);
   2438 
   2439     /* create image to hold horizontal text */
   2440     I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
   2441     if(I_in==NULL)
   2442 	return NULL;
   2443 
   2444     /* extract horizontal text */
   2445     XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
   2446 		 1, XYPixmap, I_in, 0, 0);
   2447     I_in->format=XYBitmap;
   2448 
   2449     /* magnify horizontal text */
   2450     if(style.magnify!=1.) {
   2451 	I_in=XRotMagnifyImage(dpy, I_in);
   2452 
   2453 	old_cols_in=item->cols_in;
   2454 	old_rows_in=item->rows_in;
   2455 	item->cols_in=(float)item->cols_in*style.magnify;
   2456 	item->rows_in=(float)item->rows_in*style.magnify;
   2457     }
   2458 
   2459     /* how big will rotated text be ? */
   2460     item->cols_out=fabs((float)item->rows_in*sin_angle) +
   2461 	fabs((float)item->cols_in*cos_angle) +0.99999 +2;
   2462 
   2463     item->rows_out=fabs((float)item->rows_in*cos_angle) +
   2464 	fabs((float)item->cols_in*sin_angle) +0.99999 +2;
   2465 
   2466     if(item->cols_out%2==0)
   2467 	item->cols_out++;
   2468 
   2469     if(item->rows_out%2==0)
   2470 	item->rows_out++;
   2471 
   2472     /* create image to hold rotated text */
   2473     item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
   2474     if(item->ximage==NULL)
   2475 	return NULL;
   2476 
   2477     byte_w_in=(item->cols_in-1)/8+1;
   2478     byte_w_out=(item->cols_out-1)/8+1;
   2479 
   2480     /* we try to make this bit as fast as possible - which is why it looks
   2481        a bit over-the-top */
   2482 
   2483     /* vertical distance from centre */
   2484     dj=0.5-(float)item->rows_out/2;
   2485 
   2486     /* where abouts does text actually lie in rotated image? */
   2487     if(angle==0 || angle==M_PI/2 ||
   2488        angle==M_PI || angle==3*M_PI/2) {
   2489 	xl=0;
   2490 	xr=(float)item->cols_out;
   2491 	xinc=0;
   2492     }
   2493     else if(angle<M_PI) {
   2494 	xl=(float)item->cols_out/2+
   2495 	    (dj-(float)item->rows_in/(2*cos_angle))/
   2496 		tan(angle)-2;
   2497 	xr=(float)item->cols_out/2+
   2498 	    (dj+(float)item->rows_in/(2*cos_angle))/
   2499 		tan(angle)+2;
   2500 	xinc=1./tan(angle);
   2501     }
   2502     else {
   2503 	xl=(float)item->cols_out/2+
   2504 	    (dj+(float)item->rows_in/(2*cos_angle))/
   2505 		tan(angle)-2;
   2506 	xr=(float)item->cols_out/2+
   2507 	    (dj-(float)item->rows_in/(2*cos_angle))/
   2508 		tan(angle)+2;
   2509 
   2510 	xinc=1./tan(angle);
   2511     }
   2512 
   2513     /* loop through all relevent bits in rotated image */
   2514     for(j=0; j<item->rows_out; j++) {
   2515 
   2516 	/* no point re-calculating these every pass */
   2517 	di=(float)((xl<0)?0:(int)xl)+0.5-(float)item->cols_out/2;
   2518 	byte_out=(item->rows_out-j-1)*byte_w_out;
   2519 
   2520 	/* loop through meaningful columns */
   2521 	for(i=((xl<0)?0:(int)xl);
   2522 	    i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {
   2523 
   2524 	    /* rotate coordinates */
   2525 	    it=(float)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
   2526 	    jt=(float)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
   2527 
   2528             /* set pixel if required */
   2529             if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
   2530                 if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
   2531                     item->ximage->data[byte_out+i/8]|=128>>i%8;
   2532 
   2533 	    di+=1;
   2534 	}
   2535 	dj+=1;
   2536 	xl+=xinc;
   2537 	xr+=xinc;
   2538     }
   2539     XDestroyImage(I_in);
   2540 
   2541     if(style.magnify!=1.) {
   2542 	item->cols_in=old_cols_in;
   2543 	item->rows_in=old_rows_in;
   2544     }
   2545 
   2546 
   2547 #ifdef CACHE_BITMAPS
   2548 
   2549     /* create a bitmap to hold rotated text */
   2550     item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
   2551 			       item->cols_out, item->rows_out, 1);
   2552 
   2553     /* make the text bitmap from XImage */
   2554     XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
   2555 	      item->cols_out, item->rows_out);
   2556 
   2557     XDestroyImage(item->ximage);
   2558 
   2559 #endif /*CACHE_BITMAPS*/
   2560 
   2561     XFreeGC(dpy, font_gc);
   2562     XFreePixmap(dpy, canvas);
   2563 
   2564     return item;
   2565 }
   2566 
   2567 
   2568 /* ---------------------------------------------------------------------- */
   2569 
   2570 
   2571 /**************************************************************************/
   2572 /*  Adds a text item to the end of the cache, removing as many items      */
   2573 /*      from the front as required to keep cache size below limit         */
   2574 /**************************************************************************/
   2575 
   2576 static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item)
   2577 {
   2578 
   2579     static long int current_size=0;
   2580     static RotatedTextItem *last=NULL;
   2581     RotatedTextItem *i1=first_text_item, *i2=NULL;
   2582 
   2583 #ifdef CACHE_BITMAPS
   2584 
   2585     /* I don't know how much memory a pixmap takes in the server -
   2586            probably this + a bit more we can't account for */
   2587 
   2588     item->size=((item->cols_out-1)/8+1)*item->rows_out;
   2589 
   2590 #else
   2591 
   2592     /* this is pretty much the size of a RotatedTextItem */
   2593 
   2594     item->size=((item->cols_out-1)/8+1)*item->rows_out +
   2595 	sizeof(XImage) + strlen(item->text) +
   2596 	    item->nl*8*sizeof(float) + sizeof(RotatedTextItem);
   2597 
   2598     if(item->font_name!=NULL)
   2599 	item->size+=strlen(item->font_name);
   2600     else
   2601 	item->size+=sizeof(Font);
   2602 
   2603 #endif /*CACHE_BITMAPS */
   2604 
   2605 #ifdef DEBUG
   2606     /* count number of items in cache, for debugging */
   2607     {
   2608 	int i=0;
   2609 
   2610 	while(i1) {
   2611 	    i++;
   2612 	    i1=i1->next;
   2613 	}
   2614 	DEBUG_PRINT2("Cache has %d items.\n", i);
   2615 	i1=first_text_item;
   2616     }
   2617 #endif
   2618 
   2619     DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%ld\n",
   2620 		 current_size, item->size, CACHE_SIZE_LIMIT*1024);
   2621 
   2622     /* if this item is bigger than whole cache, forget it */
   2623     if(item->size>CACHE_SIZE_LIMIT*1024) {
   2624 	DEBUG_PRINT1("Too big to cache\n\n");
   2625 	item->cached=0;
   2626 	return;
   2627     }
   2628 
   2629     /* remove elements from cache as needed */
   2630     while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {
   2631 
   2632 	DEBUG_PRINT2("Removed %d bytes\n", i1->size);
   2633 
   2634 	if(i1->font_name!=NULL)
   2635 	    DEBUG_PRINT5("  (`%s'\n   %s\n   angle=%f align=%d)\n",
   2636 			 i1->text, i1->font_name, i1->angle, i1->align);
   2637 
   2638 #ifdef CACHE_FID
   2639 	if(i1->font_name==NULL)
   2640 	    DEBUG_PRINT5("  (`%s'\n  FID=%ld\n   angle=%f align=%d)\n",
   2641                          i1->text, i1->fid, i1->angle, i1->align);
   2642 #endif /*CACHE_FID*/
   2643 
   2644 	current_size-=i1->size;
   2645 
   2646 	i2=i1->next;
   2647 
   2648 	/* free resources used by the unlucky item */
   2649 	XRotFreeTextItem(dpy, i1);
   2650 
   2651 	/* remove it from linked list */
   2652 	first_text_item=i2;
   2653 	i1=i2;
   2654     }
   2655 
   2656     /* add new item to end of linked list */
   2657     if(first_text_item==NULL) {
   2658 	item->next=NULL;
   2659 	first_text_item=item;
   2660 	last=item;
   2661     }
   2662     else {
   2663 	item->next=NULL;
   2664 	last->next=item;
   2665 	last=item;
   2666     }
   2667 
   2668     /* new cache size */
   2669     current_size+=item->size;
   2670 
   2671     item->cached=1;
   2672 
   2673     DEBUG_PRINT1("Added item to cache.\n");
   2674 }
   2675 
   2676 
   2677 /* ---------------------------------------------------------------------- */
   2678 
   2679 
   2680 /**************************************************************************/
   2681 /*  Free the resources used by a text item                                */
   2682 /**************************************************************************/
   2683 
   2684 static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item)
   2685 {
   2686     free(item->text);
   2687 
   2688     if(item->font_name!=NULL)
   2689 	free(item->font_name);
   2690 
   2691     free((char *)item->corners_x);
   2692     free((char *)item->corners_y);
   2693 
   2694 #ifdef CACHE_BITMAPS
   2695     XFreePixmap(dpy, item->bitmap);
   2696 #else
   2697     XDestroyImage(item->ximage);
   2698 #endif /* CACHE_BITMAPS */
   2699 
   2700     free((char *)item);
   2701 }
   2702 
   2703 
   2704 /* ---------------------------------------------------------------------- */
   2705 
   2706 
   2707 /**************************************************************************/
   2708 /* Magnify an XImage using bilinear interpolation                         */
   2709 /**************************************************************************/
   2710 
   2711 static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage)
   2712 {
   2713     int i, j;
   2714     float x, y;
   2715     float u,t;
   2716     XImage *I_out;
   2717     int cols_in, rows_in;
   2718     int cols_out, rows_out;
   2719     register int i2, j2;
   2720     float z1, z2, z3, z4;
   2721     int byte_width_in, byte_width_out;
   2722     float mag_inv;
   2723 
   2724     /* size of input image */
   2725     cols_in=ximage->width;
   2726     rows_in=ximage->height;
   2727 
   2728     /* size of final image */
   2729     cols_out=(float)cols_in*style.magnify;
   2730     rows_out=(float)rows_in*style.magnify;
   2731 
   2732     /* this will hold final image */
   2733     I_out=MakeXImage(dpy, cols_out, rows_out);
   2734     if(I_out==NULL)
   2735 	return NULL;
   2736 
   2737     /* width in bytes of input, output images */
   2738     byte_width_in=(cols_in-1)/8+1;
   2739     byte_width_out=(cols_out-1)/8+1;
   2740 
   2741     /* for speed */
   2742     mag_inv=1./style.magnify;
   2743 
   2744     y=0.;
   2745 
   2746     /* loop over magnified image */
   2747     for(j2=0; j2<rows_out; j2++) {
   2748 	x=0;
   2749 	j=y;
   2750 
   2751 	for(i2=0; i2<cols_out; i2++) {
   2752 	    i=x;
   2753 
   2754 	    /* bilinear interpolation - where are we on bitmap ? */
   2755 	    /* right edge */
   2756 	    if(i==cols_in-1 && j!=rows_in-1) {
   2757 		t=0;
   2758 		u=y-(float)j;
   2759 
   2760 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
   2761 		z2=z1;
   2762 		z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
   2763 		z4=z3;
   2764 	    }
   2765 	    /* top edge */
   2766 	    else if(i!=cols_in-1 && j==rows_in-1) {
   2767 		t=x-(float)i;
   2768 		u=0;
   2769 
   2770 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
   2771 		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
   2772 		z3=z2;
   2773 		z4=z1;
   2774 	    }
   2775 	    /* top right corner */
   2776 	    else if(i==cols_in-1 && j==rows_in-1) {
   2777 		u=0;
   2778 		t=0;
   2779 
   2780 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
   2781 		z2=z1;
   2782 		z3=z1;
   2783 		z4=z1;
   2784 	    }
   2785 	    /* somewhere `safe' */
   2786 	    else {
   2787 		t=x-(float)i;
   2788 		u=y-(float)j;
   2789 
   2790 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
   2791 		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
   2792 		z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
   2793 		    128>>((i+1)%8))>0;
   2794 		z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
   2795 	    }
   2796 
   2797 	    /* if interpolated value is greater than 0.5, set bit */
   2798 	    if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
   2799 		I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;
   2800 
   2801 	    x+=mag_inv;
   2802 	}
   2803 	y+=mag_inv;
   2804     }
   2805 
   2806     /* destroy original */
   2807     XDestroyImage(ximage);
   2808 
   2809     /* return big image */
   2810     return I_out;
   2811 }
   2812 
   2813 
   2814 
   2815 /* ---------------------------------------------------------------------- */
   2816 
   2817 
   2818 /**************************************************************************/
   2819 /* Calculate the bounding box some text will have when painted            */
   2820 /**************************************************************************/
   2821 static
   2822 XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, float angle, int x, int y, char *text, int align)
   2823 {
   2824     register int i;
   2825     char *str1, *str2, *str3;
   2826     char *str2_a="\0", *str2_b="\n\0";
   2827     int height;
   2828     float sin_angle, cos_angle;
   2829     int nl, max_width;
   2830     int cols_in, rows_in;
   2831     float hot_x, hot_y;
   2832     XPoint *xp_in, *xp_out;
   2833     int dir, asc, desc;
   2834     XCharStruct overall;
   2835 
   2836     /* manipulate angle to 0<=angle<360 degrees */
   2837     while(angle<0)
   2838         angle+=360;
   2839 
   2840     while(angle>360)
   2841         angle-=360;
   2842 
   2843     angle*=M_PI/180;
   2844 
   2845     /* count number of sections in string */
   2846     nl=1;
   2847     if(align!=NONE)
   2848 	for(i=0; i<strlen(text)-1; i++)
   2849 	    if(text[i]=='\n')
   2850 		nl++;
   2851 
   2852     /* ignore newline characters if not doing alignment */
   2853     if(align==NONE)
   2854 	str2=str2_a;
   2855     else
   2856 	str2=str2_b;
   2857 
   2858     /* find width of longest section */
   2859     str1=my_strdup(text);
   2860     if(str1==NULL)
   2861 	return NULL;
   2862 
   2863     str3=my_strtok(str1, str2);
   2864 
   2865     XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2866 		 &overall);
   2867 
   2868     max_width=overall.rbearing;
   2869 
   2870     /* loop through each section */
   2871     do {
   2872 	str3=my_strtok((char *)NULL, str2);
   2873 
   2874 	if(str3!=NULL) {
   2875 	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
   2876 			 &overall);
   2877 
   2878 	    if(overall.rbearing>max_width)
   2879 		max_width=overall.rbearing;
   2880 	}
   2881     }
   2882     while(str3!=NULL);
   2883 
   2884     free(str1);
   2885 
   2886     /* overall font height */
   2887     height=font->ascent+font->descent;
   2888 
   2889     /* dimensions horizontal text will have */
   2890     cols_in=max_width;
   2891     rows_in=nl*height;
   2892 
   2893     /* pre-calculate sin and cos */
   2894     sin_angle=sin(angle);
   2895     cos_angle=cos(angle);
   2896 
   2897     /* y position */
   2898     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
   2899         hot_y=(float)rows_in/2*style.magnify;
   2900     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
   2901 	hot_y=0;
   2902     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
   2903 	hot_y = -(float)rows_in/2*style.magnify;
   2904     else
   2905 	hot_y = -((float)rows_in/2-(float)font->descent)*style.magnify;
   2906 
   2907     /* x position */
   2908     if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
   2909 	hot_x = -(float)max_width/2*style.magnify;
   2910     else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
   2911 	hot_x=0;
   2912     else
   2913         hot_x=(float)max_width/2*style.magnify;
   2914 
   2915     /* reserve space for XPoints */
   2916     xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
   2917     if(!xp_in)
   2918 	return NULL;
   2919 
   2920     xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
   2921     if(!xp_out)
   2922 	return NULL;
   2923 
   2924     /* bounding box when horizontal, relative to bitmap centre */
   2925     xp_in[0].x = -(float)cols_in*style.magnify/2-style.bbx_pad;
   2926     xp_in[0].y= (float)rows_in*style.magnify/2+style.bbx_pad;
   2927     xp_in[1].x= (float)cols_in*style.magnify/2+style.bbx_pad;
   2928     xp_in[1].y= (float)rows_in*style.magnify/2+style.bbx_pad;
   2929     xp_in[2].x= (float)cols_in*style.magnify/2+style.bbx_pad;
   2930     xp_in[2].y = -(float)rows_in*style.magnify/2-style.bbx_pad;
   2931     xp_in[3].x = -(float)cols_in*style.magnify/2-style.bbx_pad;
   2932     xp_in[3].y = -(float)rows_in*style.magnify/2-style.bbx_pad;
   2933     xp_in[4].x=xp_in[0].x;
   2934     xp_in[4].y=xp_in[0].y;
   2935 
   2936     /* rotate and translate bounding box */
   2937     for(i=0; i<5; i++) {
   2938 	xp_out[i].x=(float)x + ( ((float)xp_in[i].x-hot_x)*cos_angle +
   2939 				 ((float)xp_in[i].y+hot_y)*sin_angle);
   2940 	xp_out[i].y=(float)y + (-((float)xp_in[i].x-hot_x)*sin_angle +
   2941 				 ((float)xp_in[i].y+hot_y)*cos_angle);
   2942     }
   2943 
   2944     free((char *)xp_in);
   2945 
   2946     return xp_out;
   2947 }
   2948 
   2949 
   2950 
   2951 /* ***********************************************************************
   2953  * Conversion routines for the X resource manager
   2954  * ***********************************************************************
   2955  */
   2956 
   2957 #if defined(__STDC__)
   2958 static
   2959 Boolean	strtocard(  Display *dsp,
   2960 		    XrmValue *args,
   2961 		    Cardinal *num_args,
   2962 		    XrmValue *from,
   2963 		    XrmValue *to,
   2964 		    XtPointer *unused
   2965 		    )
   2966 #else
   2967 static
   2968 Boolean	strtocard(  dsp, args, num_args, from, to, unused )
   2969 Display *dsp;
   2970 XrmValue *args;
   2971 Cardinal *num_args;
   2972 XrmValue *from;
   2973 XrmValue *to;
   2974 XtPointer *unused;
   2975 #endif
   2976 {
   2977     static Cardinal temp;
   2978 
   2979     if ( to->addr == NULL ) {
   2980 	to->addr = (XtPointer) &temp;
   2981 	to->size = sizeof(Cardinal);
   2982     }
   2983 
   2984     *((Cardinal *) to->addr) = atoi( from->addr );
   2985     return True;
   2986 }
   2987 
   2988 
   2989 #define done_bert(type, value) \
   2990     do {\
   2991 	if (to->addr != NULL) {\
   2992 	    if (to->size < sizeof(type)) {\
   2993 	        to->size = sizeof(type);\
   2994 	        return False;\
   2995 	    }\
   2996 	    *(type*)(to->addr) = (value);\
   2997         } else {\
   2998 	    static type static_val;\
   2999 	    static_val = (value);\
   3000 	    to->addr = (XtPointer)&static_val;\
   3001         }\
   3002         to->size = sizeof(type);\
   3003         return True;\
   3004     } while (0)
   3005 static
   3006 Boolean cvtStringToStringArray(Display *display, XrmValuePtr args, Cardinal *num_args, XrmValuePtr from, XrmValuePtr to, XtPointer *converter_data)
   3007 {
   3008     String t, s;
   3009     StringArray a = NULL;
   3010     Cardinal i;
   3011     char delim;
   3012 
   3013     if (*num_args != 0)
   3014 	XtAppErrorMsg(XtDisplayToApplicationContext(display),
   3015 		      "cvtStringToStringArray", "wrongParameters",
   3016 		      "XtToolkitError",
   3017 		      "String to StringArray conversion needs no arguments",
   3018 		      (String*) NULL, (Cardinal*) NULL);
   3019 
   3020     delim = ((String) from->addr)[0];
   3021     s = XtNewString((String) from->addr + 1);
   3022     i = 0;
   3023     while (s && *s) {
   3024 	t = strchr(s, delim);
   3025         if (t) *t = '\0';
   3026 	a = (StringArray) XtRealloc((String) a, (i + 1) * sizeof(*a));
   3027 	a[i] = s;
   3028 	i++;
   3029         s = t ? t + 1 : NULL;
   3030     }
   3031     a = (StringArray) XtRealloc((String) a, (i + 1) * sizeof(*a));
   3032     a[i] = NULL;
   3033     done_bert(StringArray, a);
   3034 }
   3035 
   3036 
   3037 /* ***********************************************************************
   3039  * A driver for the above in the flavor of the xt utilities module
   3040  * ***********************************************************************
   3041  */
   3042 
   3043 #define TABHT 25
   3044 
   3045 typedef struct tab_data {
   3046     Widget	form;
   3047     int		cur,
   3048 		num_tabs;
   3049     void	(*activate_func)();
   3050 } *TabData;
   3051 
   3052 
   3053 #if defined(__STDC__)
   3054 static void handle_click( Widget w, TabData td, XtPointer call_data )
   3055 #else
   3056 static void handle_click(w, td, call_data)
   3057     Widget w;
   3058     TabData td;
   3059     XtPointer call_data;
   3060 #endif
   3061 {
   3062     int tab = (int) call_data;
   3063 
   3064     /* note that the tab is relative to the current tab.
   3065      * if tab is 0, the user clicked on the current one.
   3066      * there is nothing to do
   3067      */
   3068     if (tab == 0) return;
   3069     td->cur += tab;
   3070 
   3071     /* Change tabs.  We must manually inform the UI which tab is current  */
   3072     XtVaSetValues( w,
   3073 		   XtNlefttabs, td->cur,
   3074 		   XtNrighttabs, td->num_tabs - td->cur - 1,
   3075 		   NULL
   3076 		   );
   3077 
   3078     (*td->activate_func)( td->form, td->cur );
   3079 }
   3080 
   3081 
   3082 /*
   3083  * PUBLIC: Widget __vi_CreateTabbedFolder
   3084  * PUBLIC:     __P((String, Widget, String, int, void (*)(Widget, int)));
   3085  */
   3086 #if defined(__STDC__)
   3087 Widget	__vi_CreateTabbedFolder( String name,
   3088 				Widget parent,
   3089 				String tab_labels,
   3090 				int num_tabs,
   3091 				void (*activate_func)()
   3092 				)
   3093 #else
   3094 Widget	__vi_CreateTabbedFolder( name, parent, tab_labels, num_tabs, activate_func )
   3095 String	name;
   3096 String	tab_labels;
   3097 Widget	parent;
   3098 int	num_tabs;
   3099 void	(*activate_func)();
   3100 #endif
   3101 {
   3102     Widget	tabs;
   3103     TabData	td = (TabData) malloc( sizeof(struct tab_data) );
   3104     int		i;
   3105 
   3106     XtAppSetTypeConverter(  XtDisplayToApplicationContext(XtDisplay(parent)),
   3107 			    XtRString,
   3108 			    XtRCardinal,
   3109 			    strtocard,
   3110 			    NULL,
   3111 			    0,
   3112 			    XtCacheNone,
   3113 			    NULL
   3114 			    );
   3115 
   3116     /* init our internal structure */
   3117     td->cur		= 0;
   3118     td->num_tabs	= num_tabs;
   3119     td->activate_func	= activate_func;
   3120 
   3121     /* tabs go on the top */
   3122     tabs = XtVaCreateManagedWidget( "tabs",
   3123 				     xmTabsWidgetClass,
   3124 				     parent,
   3125 				     XtNlefttabs,	0,
   3126 				     XtNrighttabs,	num_tabs-1,
   3127 				     XtNorientation,	XfwfUpTabs,
   3128 				     XmNtopAttachment,	XmATTACH_FORM,
   3129 				     XmNleftAttachment,	XmATTACH_FORM,
   3130 				     XmNleftOffset,	TABHT/4,
   3131 				     XmNrightAttachment,XmATTACH_FORM,
   3132 				     XmNrightOffset,	TABHT/4,
   3133 				     XmNbottomAttachment,XmATTACH_OPPOSITE_FORM,
   3134 				     XmNbottomOffset,	-TABHT,
   3135 				     XtNlabels,		tab_labels,
   3136 				     XtVaTypedArg,	XtNlabels,
   3137 							XtRString,
   3138 							tab_labels,
   3139 							strlen(tab_labels) + 1,
   3140 				     NULL
   3141 				     );
   3142 
   3143     /* add the callback */
   3144     XtAddCallback( tabs,
   3145 		   XtNactivateCallback,
   3146 		   (XtCallbackProc) handle_click,
   3147 		   td
   3148 		   );
   3149 
   3150     /* another form to hold the controls */
   3151     td->form = XtVaCreateWidget( "form",
   3152 				 xmFormWidgetClass,
   3153 				 parent,
   3154 				 XmNtopAttachment,	XmATTACH_WIDGET,
   3155 				 XmNtopWidget,		tabs,
   3156 				 XmNleftAttachment,	XmATTACH_FORM,
   3157 				 XmNbottomAttachment,	XmATTACH_FORM,
   3158 				 XmNrightAttachment,	XmATTACH_FORM,
   3159 				 NULL
   3160 				 );
   3161 
   3162     /* done */
   3163     return td->form;
   3164 }
   3165