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