#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <math.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xos.h>
#include <X11/Xmu/CharSet.h>
#include <X11/Xmu/Drawing.h>
#include <X11/Xmu/Misc.h>
#include <X11/Xaw/PannerP.h>
#include <X11/Xaw/XawInit.h>
#include "Private.h"
#if defined(ISC) && __STDC__ && !defined(ISC30)
extern double atof(char *);
#else
#include <stdlib.h>
#endif
static void XawPannerDestroy(Widget);
static void XawPannerInitialize(Widget, Widget, ArgList, Cardinal*);
static XtGeometryResult XawPannerQueryGeometry(Widget, XtWidgetGeometry*,
XtWidgetGeometry*);
static void XawPannerRealize(Widget, XtValueMask*, XSetWindowAttributes*);
static void XawPannerRedisplay(Widget, XEvent*, Region);
static void XawPannerResize(Widget);
static Boolean XawPannerSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
static void XawPannerSetValuesAlmost(Widget, Widget, XtWidgetGeometry*,
XtWidgetGeometry*);
static void check_knob(PannerWidget, Bool);
static void get_default_size(PannerWidget, Dimension*, Dimension*);
static Bool get_event_xy(PannerWidget, XEvent*, int*, int*);
static void move_shadow(PannerWidget);
static int parse_page_string(char*, int, int, Bool*);
static void rescale(PannerWidget);
static void reset_shadow_gc(PannerWidget);
static void reset_slider_gc(PannerWidget);
static void reset_xor_gc(PannerWidget);
static void scale_knob(PannerWidget, Bool, Bool);
static void ActionAbort(Widget, XEvent*, String*, Cardinal*);
static void ActionMove(Widget, XEvent*, String*, Cardinal*);
static void ActionNotify(Widget, XEvent*, String*, Cardinal*);
static void ActionPage(Widget, XEvent*, String*, Cardinal*);
static void ActionSet(Widget, XEvent*, String*, Cardinal*);
static void ActionStart(Widget, XEvent*, String*, Cardinal*);
static void ActionStop(Widget, XEvent*, String*, Cardinal*);
Bool XmuDistinguishablePixels(Display*, Colormap, unsigned long*, int);
static char defaultTranslations[] =
"<Btn1Down>:" "start()\n"
"<Btn1Motion>:" "move()\n"
"<Btn1Up>:" "notify() stop()\n"
"<Btn2Down>:" "abort()\n"
":<Key>KP_Enter:" "set(rubberband,toggle)\n"
"<Key>space:" "page(+1p,+1p)\n"
"<Key>Delete:" "page(-1p,-1p)\n"
":<Key>KP_Delete:" "page(-1p,-1p)\n"
"<Key>BackSpace:" "page(-1p,-1p)\n"
"<Key>Left:" "page(-.5p,+0)\n"
":<Key>KP_Left:" "page(-.5p,+0)\n"
"<Key>Right:" "page(+.5p,+0)\n"
":<Key>KP_Right:" "page(+.5p,+0)\n"
"<Key>Up:" "page(+0,-.5p)\n"
":<Key>KP_Up:" "page(+0,-.5p)\n"
"<Key>Down:" "page(+0,+.5p)\n"
":<Key>KP_Down:" "page(+0,+.5p)\n"
"<Key>Home:" "page(0,0)\n"
":<Key>KP_Home:" "page(0,0)\n"
;
static XtActionsRec actions[] = {
{"start", ActionStart},
{"stop", ActionStop},
{"abort", ActionAbort},
{"move", ActionMove},
{"page", ActionPage},
{"notify", ActionNotify},
{"set", ActionSet},
};
#define offset(field) XtOffsetOf(PannerRec, panner.field)
static XtResource resources[] = {
{
XtNallowOff,
XtCAllowOff,
XtRBoolean,
sizeof(Boolean),
offset(allow_off),
XtRImmediate,
(XtPointer)False
},
{
XtNresize,
XtCResize,
XtRBoolean,
sizeof(Boolean),
offset(resize_to_pref),
XtRImmediate,
(XtPointer)True
},
{
XtNreportCallback,
XtCReportCallback,
XtRCallback,
sizeof(XtPointer),
offset(report_callbacks),
XtRCallback,
NULL
},
{
XtNdefaultScale,
XtCDefaultScale,
XtRDimension,
sizeof(Dimension),
offset(default_scale),
XtRImmediate,
(XtPointer)PANNER_DEFAULT_SCALE
},
{
XtNrubberBand,
XtCRubberBand,
XtRBoolean,
sizeof(Boolean),
offset(rubber_band),
XtRImmediate,
(XtPointer)False
},
{
XtNforeground,
XtCForeground,
XtRPixel,
sizeof(Pixel),
offset(foreground),
XtRString,
(XtPointer)XtDefaultBackground
},
{
XtNinternalSpace,
XtCInternalSpace,
XtRDimension,
sizeof(Dimension),
offset(internal_border),
XtRImmediate,
(XtPointer)4
},
{
XtNlineWidth,
XtCLineWidth,
XtRDimension,
sizeof(Dimension),
offset(line_width),
XtRImmediate,
(XtPointer)0
},
{
XtNcanvasWidth,
XtCCanvasWidth,
XtRDimension,
sizeof(Dimension),
offset(canvas_width),
XtRImmediate,
(XtPointer)0
},
{
XtNcanvasHeight,
XtCCanvasHeight,
XtRDimension,
sizeof(Dimension),
offset(canvas_height),
XtRImmediate,
(XtPointer)0
},
{
XtNsliderX,
XtCSliderX,
XtRPosition,
sizeof(Position),
offset(slider_x),
XtRImmediate,
(XtPointer)0
},
{
XtNsliderY,
XtCSliderY,
XtRPosition,
sizeof(Position),
offset(slider_y),
XtRImmediate,
(XtPointer)0
},
{
XtNsliderWidth,
XtCSliderWidth,
XtRDimension,
sizeof(Dimension),
offset(slider_width),
XtRImmediate,
(XtPointer)0
},
{
XtNsliderHeight,
XtCSliderHeight,
XtRDimension,
sizeof(Dimension),
offset(slider_height),
XtRImmediate,
(XtPointer)0
},
{
XtNshadowColor,
XtCShadowColor,
XtRPixel,
sizeof(Pixel),
offset(shadow_color),
XtRString,
(XtPointer)XtDefaultForeground
},
{
XtNshadowThickness,
XtCShadowThickness,
XtRDimension,
sizeof(Dimension),
offset(shadow_thickness),
XtRImmediate,
(XtPointer)2
},
{
XtNbackgroundStipple,
XtCBackgroundStipple,
XtRString,
sizeof(String),
offset(stipple_name),
XtRImmediate,
NULL
},
};
#undef offset
#define Superclass (&simpleClassRec)
PannerClassRec pannerClassRec = {
{
(WidgetClass)Superclass,
"Panner",
sizeof(PannerRec),
XawInitializeWidgetSet,
NULL,
False,
XawPannerInitialize,
NULL,
XawPannerRealize,
actions,
XtNumber(actions),
resources,
XtNumber(resources),
NULLQUARK,
True,
True,
True,
False,
XawPannerDestroy,
XawPannerResize,
XawPannerRedisplay,
XawPannerSetValues,
NULL,
XawPannerSetValuesAlmost,
NULL,
NULL,
XtVersion,
NULL,
defaultTranslations,
XawPannerQueryGeometry,
XtInheritDisplayAccelerator,
NULL,
},
{
XtInheritChangeSensitive,
},
{
NULL,
}
};
WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec;
static void
reset_shadow_gc(PannerWidget pw)
{
XtGCMask valuemask = GCForeground;
XGCValues values;
unsigned long pixels[3];
if (pw->panner.shadow_gc)
XtReleaseGC((Widget)pw, pw->panner.shadow_gc);
pixels[0] = pw->panner.foreground;
pixels[1] = pw->core.background_pixel;
pixels[2] = pw->panner.shadow_color;
if (!pw->panner.stipple_name &&
!XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
pixels, 3) &&
XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
pixels, 2)) {
valuemask = GCTile | GCFillStyle;
values.fill_style = FillTiled;
values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw),
pw->panner.foreground,
pw->core.background_pixel,
pw->core.depth);
}
else {
if (!pw->panner.line_width &&
!XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
pixels, 2))
pw->panner.line_width = 1;
valuemask = GCForeground;
values.foreground = pw->panner.shadow_color;
}
if (pw->panner.line_width > 0) {
values.line_width = pw->panner.line_width;
valuemask |= GCLineWidth;
}
pw->panner.shadow_gc = XtGetGC((Widget)pw, valuemask, &values);
}
static void
reset_slider_gc(PannerWidget pw)
{
XtGCMask valuemask = GCForeground;
XGCValues values;
if (pw->panner.slider_gc)
XtReleaseGC((Widget)pw, pw->panner.slider_gc);
values.foreground = pw->panner.foreground;
pw->panner.slider_gc = XtGetGC((Widget)pw, valuemask, &values);
}
static void
reset_xor_gc(PannerWidget pw)
{
if (pw->panner.xor_gc)
XtReleaseGC((Widget)pw, pw->panner.xor_gc);
if (pw->panner.rubber_band) {
XtGCMask valuemask = (GCForeground | GCFunction);
XGCValues values;
Pixel tmp;
tmp = (pw->panner.foreground == pw->core.background_pixel ?
pw->panner.shadow_color : pw->panner.foreground);
values.foreground = tmp ^ pw->core.background_pixel;
values.function = GXxor;
if (pw->panner.line_width > 0) {
valuemask |= GCLineWidth;
values.line_width = pw->panner.line_width;
}
pw->panner.xor_gc = XtGetGC((Widget)pw, valuemask, &values);
}
else
pw->panner.xor_gc = NULL;
}
static void
check_knob(PannerWidget pw, Bool knob)
{
Position pad = pw->panner.internal_border << 1;
Position maxx = (Position)XtWidth(pw) - pad -
(Position)pw->panner.knob_width;
Position maxy = (Position)XtHeight(pw) - pad -
(Position)pw->panner.knob_height;
Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x;
Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y;
if (*x < 0)
*x = 0;
if (*x > maxx)
*x = maxx;
if (*y < 0)
*y = 0;
if (*y > maxy)
*y = maxy;
if (knob) {
pw->panner.slider_x = (Position)((double)pw->panner.knob_x
/ pw->panner.haspect + 0.5);
pw->panner.slider_y = (Position)((double)pw->panner.knob_y
/ pw->panner.vaspect + 0.5);
pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE;
}
}
static void
move_shadow(PannerWidget pw)
{
if (pw->panner.shadow_thickness > 0) {
int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1);
int pad = pw->panner.internal_border;
if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) {
XRectangle *r = pw->panner.shadow_rects;
r->x = pw->panner.knob_x + pad + pw->panner.knob_width;
r->y = pw->panner.knob_y + pad + lw;
r->width = pw->panner.shadow_thickness;
r->height = pw->panner.knob_height - lw;
r++;
r->x = pw->panner.knob_x + pad + lw;
r->y = pw->panner.knob_y + pad + pw->panner.knob_height;
r->width = pw->panner.knob_width - lw + pw->panner.shadow_thickness;
r->height = pw->panner.shadow_thickness;
pw->panner.shadow_valid = True;
return;
}
}
pw->panner.shadow_valid = False;
}
static void
scale_knob(PannerWidget pw, Bool location, Bool size)
{
if (location) {
pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x);
pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y);
}
if (size) {
Dimension width, height;
if (pw->panner.slider_width < 1)
pw->panner.slider_width = pw->panner.canvas_width;
if (pw->panner.slider_height < 1)
pw->panner.slider_height = pw->panner.canvas_height;
width = Min(pw->panner.slider_width, pw->panner.canvas_width);
height = Min(pw->panner.slider_height, pw->panner.canvas_height);
pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width);
pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height);
}
if (!pw->panner.allow_off)
check_knob(pw, True);
move_shadow(pw);
}
static void
rescale(PannerWidget pw)
{
int hpad = pw->panner.internal_border << 1;
int vpad = hpad;
if (pw->panner.canvas_width < 1)
pw->panner.canvas_width = XtWidth(pw);
if (pw->panner.canvas_height < 1)
pw->panner.canvas_height = XtHeight(pw);
if (XtWidth(pw) <= hpad)
hpad = 0;
if (XtHeight(pw) <= vpad)
vpad = 0;
pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5)
/ (double)pw->panner.canvas_width;
pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5)
/ (double)pw->panner.canvas_height;
scale_knob(pw, True, True);
}
static void
get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp)
{
Dimension pad = pw->panner.internal_border << 1;
*wp = PANNER_DSCALE(pw, pw->panner.canvas_width) + pad;
*hp = PANNER_DSCALE(pw, pw->panner.canvas_height) + pad;
}
static Bool
get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y)
{
int pad = pw->panner.internal_border;
switch (event->type) {
case ButtonPress:
case ButtonRelease:
*x = event->xbutton.x - pad;
*y = event->xbutton.y - pad;
return (True);
case KeyPress:
case KeyRelease:
*x = event->xkey.x - pad;
*y = event->xkey.y - pad;
return (True);
case EnterNotify:
case LeaveNotify:
*x = event->xcrossing.x - pad;
*y = event->xcrossing.y - pad;
return (True);
case MotionNotify:
*x = event->xmotion.x - pad;
*y = event->xmotion.y - pad;
return (True);
}
return (False);
}
static int
parse_page_string(char *s, int pagesize, int canvassize, Bool *relative)
{
char *cp;
double val = 1.0;
Bool rel = False;
for (; isascii(*s) && isspace(*s); s++)
;
if (*s == '+' || *s == '-') {
rel = True;
if (*s == '-')
val = -1.0;
s++;
}
if (!*s) {
*relative = True;
return (0);
}
for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++)
;
val *= atof(cp);
for (; isascii(*s) && isspace(*s); s++)
;
if (*s) {
switch (s[0]) {
case 'p':
case 'P':
val *= (double)pagesize;
break;
case 'c':
case 'C':
val *= (double)canvassize;
break;
}
}
*relative = rel;
return ((int)val);
}
#define DRAW_TMP(pw) \
{ \
XDrawRectangle(XtDisplay(pw), XtWindow(pw), \
pw->panner.xor_gc, \
pw->panner.tmp.x + pw->panner.internal_border, \
pw->panner.tmp.y + pw->panner.internal_border, \
pw->panner.knob_width - 1, \
pw->panner.knob_height - 1); \
pw->panner.tmp.showing = !pw->panner.tmp.showing; \
}
#define UNDRAW_TMP(pw) \
{ \
if (pw->panner.tmp.showing) \
DRAW_TMP(pw); \
}
#define BACKGROUND_STIPPLE(pw) \
XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name, \
pw->panner.shadow_color, pw->core.background_pixel, \
pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL)
#define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap)
static void
XawPannerInitialize(Widget greq, Widget gnew, ArgList args, Cardinal *num_args)
{
PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew;
Dimension defwidth, defheight;
if (req->panner.canvas_width < 1)
cnew->panner.canvas_width = 1;
if (req->panner.canvas_height < 1)
cnew->panner.canvas_height = 1;
if (req->panner.default_scale < 1)
cnew->panner.default_scale = PANNER_DEFAULT_SCALE;
get_default_size(req, &defwidth, &defheight);
if (XtWidth(req) < 1)
XtWidth(cnew) = defwidth;
if (XtHeight(req) < 1)
XtHeight(cnew) = defheight;
cnew->panner.shadow_gc = NULL;
reset_shadow_gc(cnew);
cnew->panner.slider_gc = NULL;
reset_slider_gc(cnew);
cnew->panner.xor_gc = NULL;
reset_xor_gc(cnew);
rescale(cnew);
cnew->panner.shadow_valid = False;
cnew->panner.tmp.doing = False;
cnew->panner.tmp.showing = False;
}
static void
XawPannerRealize(Widget gw, XtValueMask *valuemaskp,
XSetWindowAttributes *attr)
{
PannerWidget pw = (PannerWidget)gw;
Pixmap pm = XtUnspecifiedPixmap;
Bool gotpm = False;
if (pw->core.background_pixmap == XtUnspecifiedPixmap) {
if (pw->panner.stipple_name)
pm = BACKGROUND_STIPPLE(pw);
if (PIXMAP_OKAY(pm)) {
attr->background_pixmap = pm;
*valuemaskp |= CWBackPixmap;
*valuemaskp &= ~CWBackPixel;
gotpm = True;
}
}
(*pannerWidgetClass->core_class.superclass->core_class.realize)
(gw, valuemaskp, attr);
if (gotpm)
XFreePixmap(XtDisplay(gw), pm);
}
static void
XawPannerDestroy(Widget gw)
{
PannerWidget pw = (PannerWidget)gw;
XtReleaseGC(gw, pw->panner.shadow_gc);
XtReleaseGC(gw, pw->panner.slider_gc);
XtReleaseGC(gw, pw->panner.xor_gc);
}
static void
XawPannerResize(Widget gw)
{
rescale((PannerWidget)gw);
}
static void
XawPannerRedisplay(Widget gw, XEvent *event, Region region)
{
PannerWidget pw = (PannerWidget)gw;
Display *dpy = XtDisplay(gw);
Window w = XtWindow(gw);
int pad = pw->panner.internal_border;
Dimension lw = pw->panner.line_width;
Dimension extra = pw->panner.shadow_thickness + (lw << 1);
int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad;
if (Superclass->core_class.expose)
(Superclass->core_class.expose)(gw, event, region);
pw->panner.tmp.showing = False;
XClearArea(XtDisplay(pw), XtWindow(pw),
(int)pw->panner.last_x - ((int)lw) + pad,
(int)pw->panner.last_y - ((int)lw) + pad,
pw->panner.knob_width + extra,
pw->panner.knob_height + extra,
False);
pw->panner.last_x = pw->panner.knob_x;
pw->panner.last_y = pw->panner.knob_y;
XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky,
pw->panner.knob_width - 1, pw->panner.knob_height - 1);
if (lw)
XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky,
pw->panner.knob_width - 1, pw->panner.knob_height - 1);
if (pw->panner.shadow_valid)
XFillRectangles(dpy, w, pw->panner.shadow_gc, pw->panner.shadow_rects, 2);
if (pw->panner.tmp.doing && pw->panner.rubber_band)
DRAW_TMP(pw);
}
static Boolean
XawPannerSetValues(Widget gcur, Widget greq, Widget gnew,
ArgList args, Cardinal *num_args)
{
PannerWidget cur = (PannerWidget)gcur;
PannerWidget cnew = (PannerWidget)gnew;
Bool redisplay = False;
if (cur->panner.foreground != cnew->panner.foreground) {
reset_slider_gc(cnew);
if (cur->panner.foreground != cur->core.background_pixel)
reset_xor_gc(cnew);
redisplay = True;
}
else if (cur->panner.line_width != cnew->panner.line_width ||
cur->core.background_pixel != cnew->core.background_pixel) {
reset_xor_gc(cnew);
redisplay = True;
}
if (cur->panner.shadow_color != cnew->panner.shadow_color) {
reset_shadow_gc(cnew);
if (cur->panner.foreground == cur->core.background_pixel)
reset_xor_gc(cnew);
redisplay = True;
}
if (cur->panner.shadow_thickness != cnew->panner.shadow_thickness) {
move_shadow(cnew);
redisplay = True;
}
if (cur->panner.rubber_band != cnew->panner.rubber_band) {
reset_xor_gc(cnew);
if (cnew->panner.tmp.doing)
redisplay = True;
}
if ((cur->panner.stipple_name != cnew->panner.stipple_name
|| cur->panner.shadow_color != cnew->panner.shadow_color
|| cur->core.background_pixel != cnew->core.background_pixel)
&& XtIsRealized(gnew)) {
Pixmap pm = cnew->panner.stipple_name ?
BACKGROUND_STIPPLE(cnew) : XtUnspecifiedPixmap;
if (PIXMAP_OKAY(pm)) {
XSetWindowBackgroundPixmap(XtDisplay(cnew), XtWindow(cnew), pm);
XFreePixmap(XtDisplay(cnew), pm);
}
else
XSetWindowBackground(XtDisplay(cnew), XtWindow(cnew),
cnew->core.background_pixel);
redisplay = True;
}
if (cnew->panner.resize_to_pref &&
(cur->panner.canvas_width != cnew->panner.canvas_width
|| cur->panner.canvas_height != cnew->panner.canvas_height
|| cur->panner.resize_to_pref != cnew->panner.resize_to_pref)) {
get_default_size(cnew, &cnew->core.width, &cnew->core.height);
redisplay = True;
}
else if (cur->panner.canvas_width != cnew->panner.canvas_width
|| cur->panner.canvas_height != cnew->panner.canvas_height
|| cur->panner.internal_border != cnew->panner.internal_border) {
rescale(cnew);
redisplay = True;
}
else {
Bool loc = cur->panner.slider_x != cnew->panner.slider_x ||
cur->panner.slider_y != cnew->panner.slider_y;
Bool siz = cur->panner.slider_width != cnew->panner.slider_width ||
cur->panner.slider_height != cnew->panner.slider_height;
if (loc || siz || (cur->panner.allow_off != cnew->panner.allow_off
&& cnew->panner.allow_off)) {
scale_knob(cnew, loc, siz);
redisplay = True;
}
}
return (redisplay);
}
static void
XawPannerSetValuesAlmost(Widget gold, Widget gnew, XtWidgetGeometry *req,
XtWidgetGeometry *reply)
{
if (reply->request_mode == 0)
XawPannerResize(gnew);
(*pannerWidgetClass->core_class.superclass->core_class.set_values_almost)
(gold, gnew, req, reply);
}
static XtGeometryResult
XawPannerQueryGeometry(Widget gw, XtWidgetGeometry *intended,
XtWidgetGeometry *pref)
{
PannerWidget pw = (PannerWidget)gw;
pref->request_mode = (CWWidth | CWHeight);
get_default_size(pw, &pref->width, &pref->height);
if (((intended->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
&& intended->width == pref->width && intended->height == pref->height)
return (XtGeometryYes);
else if (pref->width == XtWidth(pw) && pref->height == XtHeight(pw))
return (XtGeometryNo);
return (XtGeometryAlmost);
}
static void
ActionStart(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
int x, y;
if (!get_event_xy(pw, event, &x, &y)) {
XBell(XtDisplay(gw), 0);
return;
}
pw->panner.tmp.doing = True;
pw->panner.tmp.startx = pw->panner.knob_x;
pw->panner.tmp.starty = pw->panner.knob_y;
pw->panner.tmp.dx = x - pw->panner.knob_x;
pw->panner.tmp.dy = y - pw->panner.knob_y;
pw->panner.tmp.x = pw->panner.knob_x;
pw->panner.tmp.y = pw->panner.knob_y;
if (pw->panner.rubber_band)
DRAW_TMP(pw);
}
static void
ActionStop(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
int x, y;
if (get_event_xy(pw, event, &x, &y)) {
pw->panner.tmp.x = x - pw->panner.tmp.dx;
pw->panner.tmp.y = y - pw->panner.tmp.dy;
if (!pw->panner.allow_off)
check_knob(pw, False);
}
if (pw->panner.rubber_band)
DRAW_TMP(pw);
pw->panner.tmp.doing = False;
}
static void
ActionAbort(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
if (!pw->panner.tmp.doing)
return;
if (pw->panner.rubber_band)
UNDRAW_TMP(pw);
if (!pw->panner.rubber_band) {
pw->panner.tmp.x = pw->panner.tmp.startx;
pw->panner.tmp.y = pw->panner.tmp.starty;
ActionNotify(gw, event, params, num_params);
}
pw->panner.tmp.doing = False;
}
static void
ActionMove(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
int x, y;
if (!pw->panner.tmp.doing)
return;
if (!get_event_xy(pw, event, &x, &y)) {
XBell(XtDisplay(gw), 0);
return;
}
if (pw->panner.rubber_band)
UNDRAW_TMP(pw);
pw->panner.tmp.x = x - pw->panner.tmp.dx;
pw->panner.tmp.y = y - pw->panner.tmp.dy;
if (!pw->panner.rubber_band)
ActionNotify(gw, event, params, num_params);
else {
if (!pw->panner.allow_off)
check_knob(pw, False);
DRAW_TMP(pw);
}
}
static void
ActionPage(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
Cardinal zero = 0;
Bool isin = pw->panner.tmp.doing;
int x, y;
Bool relx, rely;
int pad = pw->panner.internal_border << 1;
if (*num_params != 2) {
XBell(XtDisplay(gw), 0);
return;
}
x = parse_page_string(params[0], pw->panner.knob_width,
(int)XtWidth(pw) - pad, &relx);
y = parse_page_string(params[1], pw->panner.knob_height,
(int)XtHeight(pw) - pad, &rely);
if (relx)
x += pw->panner.knob_x;
if (rely)
y += pw->panner.knob_y;
if (isin) {
XEvent ev;
ev.xbutton.type = ButtonPress;
ev.xbutton.x = x;
ev.xbutton.y = y;
ActionMove(gw, &ev, NULL, &zero);
}
else {
pw->panner.tmp.doing = True;
pw->panner.tmp.x = x;
pw->panner.tmp.y = y;
ActionNotify(gw, event, NULL, &zero);
pw->panner.tmp.doing = False;
}
}
static void
ActionNotify(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
if (!pw->panner.tmp.doing)
return;
if (!pw->panner.allow_off)
check_knob(pw, False);
pw->panner.knob_x = pw->panner.tmp.x;
pw->panner.knob_y = pw->panner.tmp.y;
move_shadow(pw);
pw->panner.slider_x = (Position)((double)pw->panner.knob_x
/ pw->panner.haspect + 0.5);
pw->panner.slider_y = (Position)((double) pw->panner.knob_y
/ pw->panner.vaspect + 0.5);
if (!pw->panner.allow_off) {
Position tmp;
if (pw->panner.slider_x
> (tmp = (Position)pw->panner.canvas_width -
(Position)pw->panner.slider_width))
pw->panner.slider_x = tmp;
if (pw->panner.slider_x < 0)
pw->panner.slider_x = 0;
if (pw->panner.slider_y
> (tmp = (Position)pw->panner.canvas_height -
(Position)pw->panner.slider_height))
pw->panner.slider_y = tmp;
if (pw->panner.slider_y < 0)
pw->panner.slider_y = 0;
}
if (pw->panner.last_x != pw->panner.knob_x ||
pw->panner.last_y != pw->panner.knob_y) {
XawPannerReport rep;
XawPannerRedisplay(gw, NULL, NULL);
rep.changed = XawPRSliderX | XawPRSliderY;
rep.slider_x = pw->panner.slider_x;
rep.slider_y = pw->panner.slider_y;
rep.slider_width = pw->panner.slider_width;
rep.slider_height = pw->panner.slider_height;
rep.canvas_width = pw->panner.canvas_width;
rep.canvas_height = pw->panner.canvas_height;
XtCallCallbackList(gw, pw->panner.report_callbacks, (XtPointer)&rep);
}
}
static void
ActionSet(Widget gw, XEvent *event, String *params, Cardinal *num_params)
{
PannerWidget pw = (PannerWidget)gw;
Bool rb;
if (*num_params < 2 ||
XmuCompareISOLatin1(params[0], "rubberband") != 0) {
XBell(XtDisplay(gw), 0);
return;
}
if (XmuCompareISOLatin1(params[1], "on") == 0)
rb = True;
else if (XmuCompareISOLatin1(params[1], "off") == 0)
rb = False;
else if (XmuCompareISOLatin1(params[1], "toggle") == 0)
rb = !pw->panner.rubber_band;
else {
XBell(XtDisplay(gw), 0);
return;
}
if (rb != pw->panner.rubber_band) {
Arg args[1];
XtSetArg(args[0], XtNrubberBand, rb);
XtSetValues(gw, args, 1);
}
}