/*************************************************************
*
* Rheostat.c
* Rheostat widget implementation
*
* Author: Joe English, joe@trystero.art.com
*
*************************************************************
*
* Future enhancements:
*
* BUG: If you drag the arrow to a position outside the valid
* range too quickly, it sticks inside the dial; it should
* peg out at the minimum or maximum value. I'm not sure
* how to determine which, though...
* write query_geometry method.
* Add set(value) action
* SetValues() doesn't check for as much as it should;
* SetValues() and Initialize() need to do MUCH more range checking
* When number_intervals is is not a divisor of ValueRange, ticks
* aren't drawn where the arrow actually goes.
* Do we need a tickThickness resource?
*/
#include <stdlib.h>
#include <math.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "RheostatP.h"
#include "Rheostat.h"
/***********************************************************************
*
* Convenience macros, defaults, and declarations:
*
***********************************************************************/
#define RADIANS(d) (M_PI * (d)/ 180.0)
#define DEGREES(r) ((r) * 180.0 / M_PI)
#define DEG_TO_RAD (180.0 / M_PI)
#define RAD_TO_DEG (M_PI / 180.0)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
/* Rheostat-specific: */
#define MinAngle(w) (w->rheostat.minimum_angle)
#define MaxAngle(w) (w->rheostat.maximum_angle)
#define MinValue(w) (w->rheostat.minimum_value)
#define MaxValue(w) (w->rheostat.maximum_value)
#define AngleRange(w) (w->rheostat.maximum_angle-w->rheostat.minimum_angle)
#define ValueRange(w) (w->rheostat.maximum_value-w->rheostat.minimum_value)
#define ValueInc(w) ((w->rheostat.maximum_value-w->rheostat.minimum_value) \
/ w->rheostat.number_intervals)
#define CenterX(w) ((int)w->core.width/2)
#define CenterY(w) ((int)w->core.height/2)
/* MARGIN(w) = space from border to radius. */
#define _MARGIN(w) \
( w->rheostat.outer_margin \
+ w->rheostat.tick_length \
+ w->rheostat.dial_thickness \
+ w->rheostat.inner_margin)
#ifdef MOTIF
#define MARGIN(w) (_MARGIN(w) \
+ w->primitive.highlight_thickness \
+ w->primitive.shadow_thickness)
#else
#define MARGIN(w) _MARGIN(w)
#endif
/* Cast operator to keep lint/gcc happy: */
#define W (Widget)
/*
* Default values for various resources:
*/
#define MIN_RADIUS 2
#define DFLT_RADIUS 30
#define DFLT_OUTER 25
#define DFLT_INNER 20
#define DFLT_WIDTH 20
#define DFLT_ARROWTHICKNESS 0
/* Another good set of defaults: RADIUS=INNER=30, OUTER=15, WIDTH=10 */
#define DFLT_TICKLEN 5
#define DFLT_TICKTHICKNESS 0
#define DFLT_DIALTHICKNESS 3
#define DFLT_MINVALUE 0
#define DFLT_MAXVALUE 100
#define DFLT_MINANGLE 45
#define DFLT_MAXANGLE 315
#define DFLT_NUMTICKS 10
#define DFLT_MARGIN 2
/*
* Method functions:
*/
static void Initialize();
static void Redisplay();
static void Resize();
static void Destroy();
static Boolean SetValues();
/*
* Action functions:
*/
static void Set();
static void Notify();
static void Increment();
/*
* Private functions:
*/
static void draw_arrow (/* RheostatWidget, GC */);
static void draw_ticks (/* RheostatWidget, GC */);
static void draw_dial (/* RheostatWidget, GC */);
static void calculate_position (/* RheostatWidget */);
static void get_GCs (/* RheostatWidget */);
static void free_GCs (/* RheostatWidget */);
static void call_callbacks (/* RheostatWidget *, String, XEvent * */);
/***********************************************************************
*
* Translation, action, and resource tables:
*
***********************************************************************/
#ifdef MOTIF /* use osfXXX instead of XXX */
static char default_translations[] =
"<Btn1Down>: set() \n\
<Btn1Motion>: set() \n\
<Btn1Up>: notify() \n\
<Key>minus: increment(-1) notify() \n\
<Key>plus: increment(+1) notify() \n\
<Key>osfPageUp: increment(+1i) notify() \n\
<Key>osfPageDown: increment(-1i) notify() \n\
<Key>Return: notify() \n\
";
#else /* no "virtual keysym" braindamage */
static char default_translations[] =
"<Btn1Down>: set() \n\
<Btn1Motion>: set() \n\
<Btn1Up>: notify() \n\
<Key>minus: increment(-1) notify() \n\
<Key>plus: increment(+1) notify() \n\
<Key>Prior: increment(-1i) notify() \n\
<Key>Next: increment(+1i) notify() \n\
<Key>Home: increment(min) notify() \n\
<Key>End: increment(max) notify() \n\
<Key>Return: notify() \n\
";
#endif
static XtActionsRec actions[] = {
{ "set", (XtActionProc) Set },
{ "notify", (XtActionProc) Notify },
{ "increment", (XtActionProc) Increment }
};
static XtResource resources[] = {
# define OFFSET(x) (XtOffset(RheostatWidget, rheostat.x))
{XtNvalue, XtCValue, XtRInt, sizeof(int),
OFFSET(value), XtRImmediate, (caddr_t)0},
{XtNminimumValue, XtCMinimum, XtRInt, sizeof(int),
OFFSET(minimum_value), XtRImmediate, (caddr_t) DFLT_MINVALUE},
{XtNmaximumValue, XtCMaximum, XtRInt, sizeof(int),
OFFSET(maximum_value), XtRImmediate, (caddr_t) DFLT_MAXVALUE},
{XtNminimumAngle, XtCMinimum, XtRInt, sizeof(int),
OFFSET(minimum_angle), XtRImmediate, (caddr_t) DFLT_MINANGLE},
{XtNmaximumAngle, XtCMaximum, XtRInt, sizeof(int),
OFFSET(maximum_angle), XtRImmediate, (caddr_t) DFLT_MAXANGLE},
{XtNtickGravity, XtCGravity, XtRBoolean, sizeof(Boolean),
OFFSET(tick_gravity), XtRImmediate, (caddr_t)True},
{XtNnumberIntervals, XtCNumberIntervals, XtRInt, sizeof(int),
OFFSET(number_intervals), XtRImmediate, (caddr_t) DFLT_NUMTICKS},
{XtNresizeArrow, XtCBoolean, XtRBoolean, sizeof(Boolean),
OFFSET(resize_arrow), XtRString, "True"},
{XtNsetCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
OFFSET(set), XtRCallback, NULL},
{XtNnotify, XtCCallback, XtRCallback, sizeof(XtPointer),
OFFSET(notify), XtRCallback, NULL},
{XtNouterMargin, XtCMargin, XtRDimension, sizeof(Dimension),
OFFSET(outer_margin), XtRImmediate, (caddr_t) DFLT_MARGIN},
{XtNtickLength, XtCMargin, XtRDimension, sizeof(Dimension),
OFFSET(tick_length), XtRImmediate, (caddr_t) DFLT_TICKLEN},
{XtNdialThickness, XtCThickness, XtRDimension, sizeof(Dimension),
OFFSET(dial_thickness), XtRImmediate, (caddr_t) DFLT_DIALTHICKNESS},
{XtNinnerMargin, XtCMargin, XtRDimension, sizeof(Dimension),
OFFSET(inner_margin), XtRImmediate, (caddr_t) DFLT_MARGIN},
{XtNradius, XtCLength, XtRDimension, sizeof(Dimension),
OFFSET(radius), XtRImmediate, (caddr_t) DFLT_RADIUS},
{XtNouterArrowLength, XtCLength, XtRDimension, sizeof(Dimension),
OFFSET(outer_arrow_length), XtRImmediate, (caddr_t) DFLT_OUTER},
{XtNinnerArrowLength, XtCLength, XtRDimension, sizeof(Dimension),
OFFSET(inner_arrow_length), XtRImmediate, (caddr_t) DFLT_INNER},
{XtNarrowWidth, XtCWidth, XtRDimension, sizeof(Dimension),
OFFSET(arrow_width), XtRImmediate, (caddr_t) DFLT_WIDTH},
{XtNfillArrow, XtCBoolean, XtRBoolean, sizeof(Boolean),
OFFSET(fill_arrow), XtRString, "False" },
{XtNarrowThickness, XtCThickness, XtRDimension, sizeof(Dimension),
OFFSET(arrow_thickness), XtRImmediate, (caddr_t) DFLT_ARROWTHICKNESS},
{XtNtickThickness, XtCThickness, XtRDimension, sizeof(Dimension),
OFFSET(tick_thickness), XtRImmediate, (caddr_t) DFLT_TICKTHICKNESS},
#ifdef MOTIF
{XtNuseShadowColors, XtCBoolean, XtRBoolean, sizeof(Boolean),
OFFSET(use_shadow_colors), XtRString, "False"},
#endif
{XtNarrowColor, XtCForeground, XtRPixel, sizeof(Pixel),
OFFSET(arrow_pixel), XtRString, XtDefaultForeground},
{XtNdialColor, XtCForeground, XtRPixel, sizeof(Pixel),
OFFSET(dial_pixel), XtRString, XtDefaultForeground},
{XtNtickColor, XtCForeground, XtRPixel, sizeof(Pixel),
OFFSET(tick_pixel), XtRString, XtDefaultForeground},
# undef OFFSET
};
RheostatClassRec rheostatClassRec =
{
/* CoreClassPart */
{
#ifdef MOTIF
/* superclass */ (WidgetClass) &xmPrimitiveClassRec,
#else
/* superclass */ (WidgetClass) &widgetClassRec,
#endif
/* class_name */ "Rheostat",
/* widget_size */ sizeof(RheostatRec),
/* class_initialize */ NULL,
/* class_part_initialize */ NULL,
/* class_inited */ FALSE,
/* initialize */ Initialize,
/* initialize_hook */ NULL,
/* realize */ XtInheritRealize,
/* actions */ actions,
/* num_actions */ XtNumber(actions),
/* resources */ resources,
/* num_resources */ XtNumber(resources),
/* xrm_class */ NULLQUARK,
/* compress_motion */ TRUE,
/* compress_exposure */ TRUE,
/* compress_enterleave */ TRUE,
/* visible_interest */ TRUE,
/* destroy */ Destroy,
/* resize */ Resize,
/* expose */ Redisplay,
/* set_values */ SetValues,
/* set_values_hook */ NULL,
/* set_values_almost */ XtInheritSetValuesAlmost,
/* get_values_hook */ NULL,
/* accept_focus */ NULL,
/* version */ XtVersion,
/* callback private */ NULL,
/* tm_table */ default_translations,
/* query_geometry */ XtInheritQueryGeometry,
/* display_accelerator */ XtInheritDisplayAccelerator,
/* extension */ NULL
},
#ifdef MOTIF
/* Primitive class fields */
{
/* border_highlight */ (XtWidgetProc) _XtInherit,
/* border_unhighlight */ (XtWidgetProc) _XtInherit,
/* translations */ XtInheritTranslations,
/* arm_and_activate */ (XmArmAndActivate)Notify,
/* syn_resources */ NULL,
/* num_syn_resources */ 0,
/* extension */ NULL
},
#endif
/* Rheostat class fields */
{
/* ignore */ 0
}
};
WidgetClass rheostatWidgetClass = (WidgetClass) &rheostatClassRec;
/***********************************************************************
*
* Method functions:
*
**********************************************************************/
/*
* Initialize method:
*/
static void Initialize(request, new)
RheostatWidget request, new;
{
int margin = MARGIN(new);
int user_radius = new->rheostat.radius; /* request from user */
int size_radius = /* calculated from size */
MIN(new->core.height,new->core.width)/2 - margin;
int min_radius = /* from arrow dimens */
MAX(new->rheostat.inner_arrow_length,new->rheostat.outer_arrow_length);
int min_dimen;
/*
* Check geometry:
* Set radius from user value, else widget size, else default.
* Make sure radius is >= inner length & outer length.
* %%% This is a bit restrictive -- e.g., r=10,i=10,o=12 should be allowed
* Make sure width and height are >= 2*(radius + margins)
*/
if (user_radius != 0)
new->rheostat.radius = user_radius;
else if (new->core.width != 0 && new->core.height != 0)
new->rheostat.radius = size_radius;
else
new->rheostat.radius = DFLT_RADIUS;
/* Make sure radius is large enough: */
if (new->rheostat.radius < min_radius)
new->rheostat.radius = min_radius;
/* Make sure widget is large enough: */
min_dimen = 2*(new->rheostat.radius+margin);
if (new->core.width < min_dimen)
new->core.width = min_dimen;
if (new->core.height < min_dimen)
new->core.height = min_dimen;
#ifdef MOTIF
if (new->rheostat.use_shadow_colors) {
new->rheostat.arrow_pixel = new->primitive.bottom_shadow_color;
new->rheostat.dial_pixel = new->primitive.top_shadow_color;
}
#endif
get_GCs(new);
calculate_position(new);
new->rheostat.orig_radius = new->rheostat.radius;
new->rheostat.orig_outer_length = new->rheostat.outer_arrow_length;
new->rheostat.orig_inner_length = new->rheostat.inner_arrow_length;
new->rheostat.orig_width = new->rheostat.arrow_width;
}
/*
* Destroy method:
*/
static void Destroy(w)
RheostatWidget w;
{
free_GCs(w);
}
/*
* Resize method:
*/
static void Resize(w)
RheostatWidget w;
{
int newr;
newr =
(w->core.height < w->core.width
? w->core.height : w->core.width) / 2 - MARGIN(w);
if (newr < 2)
newr = 2;
if (w->rheostat.resize_arrow) {
int oldr = w->rheostat.orig_radius;
w->rheostat.outer_arrow_length =
(w->rheostat.orig_outer_length * newr) / oldr;
w->rheostat.inner_arrow_length =
(w->rheostat.orig_inner_length * newr) / oldr;
w->rheostat.arrow_width = (w->rheostat.orig_width * newr) / oldr;
}
w->rheostat.radius = newr;
calculate_position(w);
}
/*
* Expose method:
*/
static void Redisplay(w, event, region)
RheostatWidget w;
XEvent *event;
Region region;
{
# ifdef MOTIF
int hlt = w->primitive.highlight_thickness;
_XmDrawShadow(XtDisplay(w), XtWindow(w),
w->primitive.top_shadow_GC, w->primitive.bottom_shadow_GC,
w->primitive.shadow_thickness,
hlt, hlt, w->core.width - 2*hlt, w->core.height - 2*hlt);
# endif
draw_ticks(w, w->rheostat.tick_GC);
draw_dial(w, w->rheostat.dial_GC);
draw_arrow(w, w->rheostat.arrow_GC);
}
/*
* SetValues:
*/
static Boolean SetValues(current, request, new)
RheostatWidget current, request, new;
{
Boolean redraw = FALSE; /* TRUE=>widget needs to be redrawn */
Boolean recalc = FALSE; /* TRUE=>arrow position changed */
# define CHECK(fld) (new->fld != current->fld)
/*
* Check rheostat parameters:
*/
if ( CHECK(rheostat.value)
|| CHECK(rheostat.minimum_value) || CHECK(rheostat.maximum_value)
|| CHECK(rheostat.maximum_angle) || CHECK(rheostat.minimum_angle)
)
{
recalc=TRUE;
redraw=TRUE;
}
/*
* Bounds check:
*/
if (new->rheostat.value > new->rheostat.maximum_value)
new->rheostat.value = new->rheostat.maximum_value;
if (new->rheostat.value < new->rheostat.minimum_value)
new->rheostat.value = new->rheostat.minimum_value;
/*
* Margin, geometry parameters -- may affect radius;
*/
if ( CHECK(rheostat.outer_margin)
|| CHECK(rheostat.dial_thickness)
|| CHECK(rheostat.inner_margin)
#ifdef MOTIF
|| CHECK(primitive.shadow_thickness)
|| CHECK(primitive.highlight_thickness)
#endif
)
{
int newr =
(new->core.height < new->core.width
? new->core.height : new->core.width) / 2 - MARGIN(new);
if (newr < MIN_RADIUS)
newr = MIN_RADIUS;
new->rheostat.radius = newr;
recalc=TRUE;
}
/*
* Radius and arrow sizes:
*/
if ( CHECK(rheostat.radius)
|| CHECK(rheostat.outer_arrow_length)
|| CHECK(rheostat.inner_arrow_length)
|| CHECK(rheostat.arrow_width)
)
{
new->rheostat.orig_radius = new->rheostat.radius;
new->rheostat.orig_outer_length = new->rheostat.outer_arrow_length;
new->rheostat.orig_inner_length = new->rheostat.inner_arrow_length;
new->rheostat.orig_width = new->rheostat.arrow_width;
recalc = TRUE;
redraw = TRUE;
}
/*
* Check for color change:
*/
if ( CHECK(core.background_pixel)
|| CHECK(rheostat.tick_pixel)
|| CHECK(rheostat.dial_pixel)
|| CHECK(rheostat.arrow_pixel)
|| CHECK(rheostat.arrow_thickness)
|| CHECK(rheostat.dial_thickness)
)
{
get_GCs(new);
free_GCs(current);
redraw = TRUE;
}
/*
* Other display resources:
*/
if ( CHECK(rheostat.number_intervals)
|| CHECK(rheostat.fill_arrow)
)
{
redraw = TRUE;
}
/*
* Wrap up:
*/
# undef CHECK
if (recalc)
calculate_position(new);
return redraw;
}
/***********************************************************************
*
* Action functions:
*
***********************************************************************/
/*ARGSUSED*/
static void Set(w, event, params, nparams)
RheostatWidget w;
XEvent *event;
String *params;
Cardinal *nparams;
{
if (event->type == ButtonPress || event->type == MotionNotify)
{
int x,y,v;
double theta,length,radius;
x = event->xbutton.x - CenterX(w);
y = event->xbutton.y - CenterY(w);
if (!x && !y) /* click at center of widget -- no angle */
return;
/* else */
radius = sqrt((double)(x*x + y*y));
/*
* Calculate value in range 0 .. 360
*/
theta = DEGREES(atan2((double)(-x), (double)(y)));
theta -= w->rheostat.minimum_angle;
if (theta < 0.0)
theta += 360.0;
v = (int)(
theta * (double)ValueRange(w)
/ (double)AngleRange(w)
) + w->rheostat.minimum_value;
/*
* If tick_gravity is on, and click is in tick region,
* snap to nearest increment:
*/
length = (double)(w->rheostat.radius + w->rheostat.inner_margin);
if ( w->rheostat.tick_gravity
&& radius >= length
&& radius <= length
+ w->rheostat.dial_thickness + w->rheostat.tick_length)
{
double inc = ValueInc(w);
v = (v / inc + 0.5);
v *= inc;
}
/*
* Bounds-check:
* Note: v should never be < minimum_value.
*/
if (v > w->rheostat.maximum_value || v < w->rheostat.minimum_value)
return;
draw_arrow(w,w->rheostat.eraser_GC);
w->rheostat.value = v;
calculate_position(w);
draw_arrow(w,w->rheostat.arrow_GC);
}
call_callbacks(w, XtNsetCallback, event);
}
static void Increment(w, event, params, nparams)
RheostatWidget w;
XEvent *event;
String *params;
Cardinal *nparams;
{
double inc = ValueInc(w);
Boolean snap = False;
double v;
/*
* Figure out increment from parameter:
*/
v = w->rheostat.value;
if (*nparams != 1) { /* default: step to nearest interval: */
v += inc;
snap = True;
} else {
if (!strcmp(params[0],"max")) v = w->rheostat.maximum_value;
else if (!strcmp(params[0],"min")) v = w->rheostat.minimum_value;
else if (!strcmp(params[0],"+1i")) { v += inc; snap = True; }
else if (!strcmp(params[0],"-1i")) { v -= inc; snap = True; }
else v += atof(params[0]);
}
if (snap)
v = (int)(v / inc + 0.5) * inc;
/*
* Bounds-check:
*/
if (v > w->rheostat.maximum_value)
v = w->rheostat.maximum_value;
if (v < w->rheostat.minimum_value)
v = w->rheostat.minimum_value;
draw_arrow(w,w->rheostat.eraser_GC);
w->rheostat.value = v;
calculate_position(w);
draw_arrow(w,w->rheostat.arrow_GC);
call_callbacks(w,XtNsetCallback,event);
}
/*ARGSUSED*/
static void Notify(w, event, params, nparams)
RheostatWidget w;
XEvent *event;
String *params;
Cardinal *nparams;
{
call_callbacks(w, XtNnotify, event);
}
/***********************************************************************
*
* Utility routines:
*
***********************************************************************/
static void call_callbacks(w, callback_name, event)
RheostatWidget w;
char *callback_name;
XEvent *event;
{
RheostatCallbackStruct cb;
cb.reason = 0; /* this is never used, even under Motif */
cb.event = event;
cb.value = w->rheostat.value;
XtCallCallbacks(W w, callback_name, (XtPointer)&cb);
}
static void draw_arrow(w, gc)
RheostatWidget w;
GC gc;
{
XfwfDrawArrow(XtDisplay(w), XtWindow(w), gc,
w->rheostat.tip_x, w->rheostat.tip_y,
w->rheostat.tip_x - CenterX(w), w->rheostat.tip_y - CenterY(w),
w->rheostat.outer_arrow_length,
w->rheostat.inner_arrow_length,
w->rheostat.arrow_width,
w->rheostat.fill_arrow);
}
static void draw_dial(w, gc)
RheostatWidget w;
GC gc;
{
int radius = w->rheostat.radius
+ w->rheostat.inner_margin
+(w->rheostat.dial_thickness+1) / 2;
XDrawArc(XtDisplay(w), XtWindow(w), w->rheostat.dial_GC,
CenterX(w) - radius,
CenterY(w) - radius,
2*radius, 2*radius,
64 * ((270 - w->rheostat.minimum_angle + 360) % 360),
64 * -AngleRange(w)
);
}
static void draw_ticks(w, gc)
RheostatWidget w;
GC gc;
{
int i,cx,cy;
double theta,inc;
double ro,ri; /* inner & outer radii of ticks */
if (!w->rheostat.number_intervals)
return;
/*
* %%% should check if minimum_angle == maximum_angle (mod 360),
* so the last tick doesn't coincide with the first.
*/
inc = RADIANS((double)AngleRange(w))/(double)(w->rheostat.number_intervals);
ri = (double)(w->rheostat.radius + w->rheostat.inner_margin
+ (w->rheostat.dial_thickness+1)/2);
ro = ri + w->rheostat.tick_length + (w->rheostat.dial_thickness+1)/2;
cx = CenterX(w);
cy = CenterY(w);
/*
* Draw segments:
*/
theta = RADIANS((double)MinAngle(w));
i = w->rheostat.number_intervals + 1;
while (i--) {
double c = cos(theta);
double s = sin(theta);
XDrawLine(XtDisplay(w),XtWindow(w),gc,
cx - (int)(ro * s), cy + (int)(ro * c),
cx - (int)(ri * s), cy + (int)(ri * c));
theta += inc;
}
}
static void calculate_position(w)
RheostatWidget w;
{
double theta,length;
length = (double)w->rheostat.radius;
/*
* Calculate angle: theta = V*(maxTheta-minTheta) / (maxV - minV) + minTheta
*/
theta = (double)(w->rheostat.value)
* (double)(AngleRange(w))
/ (double)(ValueRange(w))
+ (double)(MinAngle(w));
theta = RADIANS(theta);
w->rheostat.tip_x = CenterX(w) - (int)(length * sin(theta));
w->rheostat.tip_y = CenterY(w) + (int)(length * cos(theta));
}
/*
* get_GCs
* allocate foreground & background. GCs.
*/
static void get_GCs(w)
RheostatWidget w;
{
XGCValues values;
XtGCMask mask;
/*
* dial:
*/
mask = GCForeground | GCBackground | GCLineWidth | GCCapStyle;
values.foreground = w->rheostat.dial_pixel;
values.background = w->core.background_pixel;
values.line_width = w->rheostat.dial_thickness;
values.cap_style = CapRound;
w->rheostat.dial_GC = XtGetGC(W w, mask, &values);
/*
* tick marks:
*/
mask = GCForeground | GCBackground | GCFunction | GCLineWidth | GCCapStyle;
values.foreground = w->rheostat.tick_pixel;
values.background = w->core.background_pixel;
values.function = GXcopy;
values.line_width = w->rheostat.tick_thickness;
values.cap_style = CapRound;
w->rheostat.tick_GC = XtGetGC(W w, mask, &values);
/*
* Arrow:
*/
mask = GCForeground | GCBackground | GCFunction | GCLineWidth
| GCCapStyle | GCFillStyle;
values.foreground = w->rheostat.arrow_pixel;
values.background = w->core.background_pixel;
values.line_width = w->rheostat.arrow_thickness;
values.cap_style = CapRound;
values.fill_style = FillSolid;
w->rheostat.arrow_GC = XtGetGC(W w, mask, &values);
/*
* Eraser (identical to Arrow except fg & bg pixels are swapped):
*/
values.foreground = w->core.background_pixel;
values.background = w->rheostat.arrow_pixel;
w->rheostat.eraser_GC = XtGetGC(W w, mask, &values);
return;
}
static void free_GCs(w)
RheostatWidget w;
{
XtReleaseGC(W w,w->rheostat.arrow_GC);
XtReleaseGC(W w,w->rheostat.eraser_GC);
XtReleaseGC(W w,w->rheostat.dial_GC);
XtReleaseGC(W w,w->rheostat.tick_GC);
return;
}
void XfwfDrawArrow(dpy, d, gc, endx, endy, dx, dy,
outer_length, inner_length, width, fill)
Display *dpy;
Drawable d;
GC gc;
Position endx, endy; /* position of arrow tip */
int dx, dy; /* slope of arrow */
Dimension outer_length; /* distance tip->base */
Dimension inner_length; /* distance tip->inner */
Dimension width; /* distance base->outer points */
Boolean fill; /* True=>fill arrow,False=>outline */
{
XPoint points[5];
float scalef = sqrt((double)(dx*dx+dy*dy)); /* normalization factor */
int al = (int)outer_length,
bl = (int)inner_length,
aw = (int)width / 2,
lx = al * dx / scalef, /* distance from tip to base */
ly = al * dy / scalef,
mx = bl * dx / scalef, /* distance from tip to middle point */
my = bl * dy / scalef,
wx = aw * -dy / scalef, /* distance from base to outer points */
wy = aw * dx / scalef;
points[0].x = endx; points[0].y = endy;
points[1].x = endx-lx + wx; points[1].y = endy-ly + wy;
points[2].x = endx-mx; points[2].y = endy-my;
points[3].x = endx-lx - wx; points[3].y = endy-ly - wy;
points[4].x = endx; points[4].y = endy;
if (fill)
XFillPolygon(dpy,d,gc,
points,5,
inner_length <= outer_length ? Nonconvex : Convex,
CoordModeOrigin);
else
XDrawLines(dpy,d,gc,points,5,CoordModeOrigin);
}
/***********************************************************************
*
* Public functions:
*
***********************************************************************/
/* RheostatSetIntCallback()
* General-purpose callback function for Rheostat widgets;
* gets the position of the Rheostat, sets *(int *)closure
*/
void XfwfRheostatSetIntCallback(w,closure,call_data)
Widget w;
XtPointer closure;
XtPointer call_data;
{
*((int *)closure) = ((RheostatCallbackStruct *)call_data)->value;
}
syntax highlighted by Code2HTML, v. 0.9.1