/* $XConsortium: Panner.c /main/5 1995/07/15 20:41:11 drk $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ /* The following copyright notice solves CR 7093. */ /* * * Copyright 1989 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /************************************ WARNING ********************************** * * ExmPanner is a demonstration widget. OSF provides this widget * solely to teach programmers how to write their own Motif widgets. * OSF does not support this widget in any way * ************************************ WARNING *********************************/ /************************************************************************* * * Panner.c - ExmPanner widget. This widget lets a user pan a * ScrolledWindow (or any other scrollFrame widget) * in two-dimensions. The ExmPanner widget is similar * to the Athena Panner widget. ExmPanner demonstrates: * * how to install the XmQTnavigator trait. * *********************************************************************/ #include #include #include #include #include #include /* Define macros and constants. */ static XmConst int DEFAULT_WIDTH=100; static XmConst int DEFAULT_HEIGHT=100; static XmConst int DEFAULT_CANVAS_WIDTH=100; static XmConst int DEFAULT_CANVAS_HEIGHT=100; static XmConst int PANNER_OUTOFRANGE = (-30000); #define PANNER_HSCALE(pw,val) ((pw)->panner.haspect * ((double) (val))) #define PANNER_VSCALE(pw,val) ((pw)->panner.vaspect * ((double) (val))) static void ClassPartInitialize( WidgetClass wc ); static void Initialize( Widget greq, Widget gnew, ArgList args, Cardinal *num_args); static void Resize ( Widget gw); static Boolean SetValues( Widget gcur, Widget greq, Widget gnew, ArgList args, Cardinal *num_args); static void DrawVisual( Widget gw); static void CreateGC( Widget w); static void DestroyGC( Widget w); static void DrawRubberBand( ExmPannerWidget pw); static void CheckKnob( register ExmPannerWidget pw, Boolean knob); static void ScaleKnob ( ExmPannerWidget pw, Boolean location, Boolean size); static void ExmPannerStart ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void ExmPannerStop ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void ExmPannerAbort ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void ExmPannerMove ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void ExmPannerNotify ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void ExmPannerSet ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static Boolean GetEventXY ( ExmPannerWidget pw, XEvent *event, int *x, int *y); static int ParsePageString ( register char *s, int pagesize, int canvassize, Boolean *relative); static void ExmPannerPage ( Widget gw, XEvent *event, String *params, Cardinal *num_params); static void NavigChangeMoveCB( Widget nav, XtCallbackProc moveCB, XtPointer closure, Boolean setunset); static void NavigSetValue( Widget nav, XmNavigatorData nav_data, Boolean notify); static void NavigGetValue( Widget nav, XmNavigatorData nav_data); /* Define the translations. */ static char defaultTranslations[] = ": PrimitiveUnmap()\n\ : PrimitiveEnter()\n\ : PrimitiveLeave()\n\ : PrimitiveFocusIn()\n\ : PrimitiveFocusOut()\n\ :osfActivate: PrimitiveParentActivate()\n\ :osfHelp: PrimitiveHelp()\n\ :osfCancel: ExmPannerAbort()\n\ ~s ~c ~m ~a : ExmPannerStart(1) \n\ ~s ~c ~m ~a : ExmPannerMove() \n\ ~s ~c ~m ~a : ExmPannerNotify() ExmPannerStop() \n\ ~s ~c ~m ~a : ExmPannerStart(2) \n\ ~s ~c ~m ~a : ExmPannerMove() \n\ ~s ~c ~m ~a : ExmPannerNotify() ExmPannerStop() \n\ :space: ExmPannerSet(rubberband,toggle) \n\ :osfPageUp: ExmPannerPage(+0, -1p) \n\ :c osfPageUp: ExmPannerPage(+0, -1c) \n\ :osfPageDown: ExmPannerPage(+0, +1p) \n\ :c osfPageDown: ExmPannerPage(+0, +1c) \n\ :osfPageLeft: ExmPannerPage(-1p, +0) \n\ :c osfPageLeft: ExmPannerPage(-1c, +0) \n\ :osfPageRight: ExmPannerPage(+1p, +0) \n\ :c osfPageRight: ExmPannerPage(+1c, +0) \n\ :osfLeft: ExmPannerPage(-1, +0) \n\ :c osfLeft: ExmPannerPage(-1p, +0) \n\ :osfRight: ExmPannerPage(+1, +0) \n\ :c osfRight: ExmPannerPage(+1p, +0) \n\ :osfUp: ExmPannerPage(+0, -1) \n\ :c osfUp: ExmPannerPage(+0, -1p) \n\ :osfDown: ExmPannerPage(+0, +1) \n\ :c osfDown: ExmPannerPage(+0, +1p) \n\ :osfEndLine: ExmPannerPage(+1c, +1c) \n\ :osfBeginLine: ExmPannerPage(0,0)\n\ ~s ~m ~a Return: PrimitiveParentActivate()\n\ s ~m ~a Tab: PrimitivePrevTabGroup()\n\ ~m ~a Tab: PrimitiveNextTabGroup()"; /* Define the actions array. */ static XtActionsRec actions[] = { {"ExmPannerStart", ExmPannerStart}, /* start tmp graphics */ {"ExmPannerStop", ExmPannerStop}, /* stop tmp graphics */ {"ExmPannerAbort", ExmPannerAbort}, /* abort panning */ {"ExmPannerMove", ExmPannerMove}, /* move tmp graphics on Motion event */ {"ExmPannerPage", ExmPannerPage}, /* page around usually from keyboard */ {"ExmPannerNotify",ExmPannerNotify}, /* callback new position */ {"ExmPannerSet", ExmPannerSet} /* set various parameters */ }; /* Define the resources. */ #define poff(field) XtOffsetOf(ExmPannerRec, panner.field) static XtResource resources[] = { { ExmNreportCallback, ExmCReportCallback, XtRCallback, sizeof(XtPointer), poff(report_callbacks), XtRCallback, (XtPointer) NULL }, { ExmNrubberBand, ExmCRubberBand, XtRBoolean, sizeof(Boolean), poff(rubber_band), XtRImmediate, (XtPointer) FALSE }, { ExmNcanvasWidth, ExmCCanvasWidth, XmRHorizontalDimension, sizeof(Dimension), poff(canvas_width), XtRImmediate, (XtPointer) 0 }, { ExmNcanvasHeight, ExmCCanvasHeight, XmRVerticalDimension, sizeof(Dimension), poff(canvas_height), XtRImmediate, (XtPointer) 0 }, { ExmNsliderX, ExmCSliderX, XmRHorizontalPosition, sizeof(Position), poff(slider_x), XtRImmediate, (XtPointer) 0 }, { ExmNsliderY, ExmCSliderY, XmRVerticalPosition, sizeof(Position), poff(slider_y), XtRImmediate, (XtPointer) 0 }, { ExmNsliderWidth, ExmCSliderWidth, XmRHorizontalDimension, sizeof(Dimension), poff(slider_width), XtRImmediate, (XtPointer) 0 }, { ExmNsliderHeight, ExmCSliderHeight, XmRVerticalDimension, sizeof(Dimension), poff(slider_height), XtRImmediate, (XtPointer) 0 }, { XmNnavigationType, XmCNavigationType, XmRNavigationType, sizeof(unsigned char), XtOffsetOf(ExmPannerRec, primitive.navigation_type), XmRImmediate, (XtPointer) XmSTICKY_TAB_GROUP }, }; /* 6 of the 8 resources have corresponding synthetic resources. */ static XmSyntheticResource syn_resources[] = { { ExmNcanvasWidth, sizeof (Dimension), poff(canvas_width), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { ExmNcanvasHeight, sizeof (Dimension), poff(canvas_height), XmeFromVerticalPixels, XmeToVerticalPixels }, { ExmNsliderX, sizeof (Position), poff(slider_x), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { ExmNsliderY, sizeof (Position), poff(slider_y), XmeFromVerticalPixels, XmeToVerticalPixels }, { ExmNsliderWidth, sizeof (Dimension), poff(slider_width), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { ExmNsliderHeight, sizeof (Dimension), poff(slider_height), XmeFromVerticalPixels, XmeToVerticalPixels }, }; #undef poff /* Define the widget class record. */ externaldef(exmpannerclassrec) ExmPannerClassRec exmPannerClassRec = { { /* Following is the Core class record. */ /* superclass */ (WidgetClass) &exmSimpleClassRec, /* class_name */ "ExmPanner", /* widget_size */ sizeof(ExmPannerRec), /* class_initialize */ NULL, /* class_part_initialize */ ClassPartInitialize, /* 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 */ FALSE, /* destroy */ NULL, /* resize */ Resize, /* expose */ XtInheritExpose, /* 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 */ defaultTranslations, /* query_geometry */ NULL, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL }, { /* Following is the XmPrimitive class record. */ /* border_highlight */ XmInheritWidgetProc, /* border_unhighlight */ XmInheritWidgetProc, /* translations */ NULL, /* arm_and_activate */ (XtActionProc)NULL, /* syn_resources */ syn_resources, /* num_syn_resources */ XtNumber(syn_resources), /* extension */ NULL, }, { /* Following is the ExmSimple class record. */ /* draw_visual */ DrawVisual, /* draw_shadow */ NULL, /* create_gc */ CreateGC, /* destroy_gc */ DestroyGC, /* select_gc */ ExmInheritSelectGC, /* calc_visual_size */ ExmInheritCalcVisualSize, /* calc_widget_size */ ExmInheritCalcWidgetSize, /* extension */ NULL, }, { /* Following is the ExmPanner class record. */ /* extension */ NULL } }; externaldef(exmpannerwidgetclass) WidgetClass exmPannerWidgetClass = (WidgetClass) &exmPannerClassRec; /* Navigator Trait record for Panner */ static XmConst XmNavigatorTraitRec pannerNT = { 0, /* version */ NavigChangeMoveCB, NavigSetValue, NavigGetValue, }; /********************************************************************* * * ClassPartInitialize * Initialize navigator trait * *********************************************************************/ static void ClassPartInitialize( WidgetClass wc ) { /* Install the navigator trait. */ XmeTraitSet((XtPointer)wc, XmQTnavigator, (XtPointer) &pannerNT); } /********************************************************************* * * Initialize * *********************************************************************/ static void Initialize( Widget greq, Widget gnew, ArgList args, Cardinal *num_args) { ExmPannerWidget req = (ExmPannerWidget) greq, new = (ExmPannerWidget) gnew; if (req->panner.canvas_width < 1) new->panner.canvas_width = DEFAULT_CANVAS_WIDTH; if (req->panner.canvas_height < 1) new->panner.canvas_height = DEFAULT_CANVAS_HEIGHT; if (greq->core.width == 0) gnew->core.width = DEFAULT_WIDTH; if (greq->core.height == 0) gnew->core.height = DEFAULT_HEIGHT; new->panner.tmp.doing = FALSE; new->panner.tmp.showing = FALSE; /* call the resize method to get an initial size */ (* (gnew->core.widget_class->core_class.resize)) (gnew); /* Simple Initialize already created the GCs */ } /********************************************************************* * * Resize * *********************************************************************/ static void Resize ( Widget gw) { ExmPannerWidget pw = (ExmPannerWidget)gw; int hpad = pw->simple.margin_width * 2; int vpad = pw->simple.margin_height * 2; if (pw->panner.canvas_width < 1) pw->panner.canvas_width = pw->core.width; if (pw->panner.canvas_height < 1) pw->panner.canvas_height = pw->core.height; if ((int)pw->core.width <= hpad) hpad = 0; if ((int)pw->core.height <= vpad) vpad = 0; pw->panner.haspect = ((double) pw->core.width - hpad) / (double) pw->panner.canvas_width; pw->panner.vaspect = ((double) pw->core.height - vpad) / (double) pw->panner.canvas_height; ScaleKnob (pw, TRUE, TRUE); } /********************************************************************* * * SetValues * *********************************************************************/ static Boolean SetValues( Widget gcur, Widget greq, Widget gnew, ArgList args, Cardinal *num_args) { ExmPannerWidget cur = (ExmPannerWidget) gcur; ExmPannerWidget new = (ExmPannerWidget) gnew; Boolean redisplay = FALSE; if (cur->panner.rubber_band != new->panner.rubber_band) { if (new->panner.tmp.doing) redisplay = TRUE; } if (cur->panner.canvas_width != new->panner.canvas_width || cur->panner.canvas_height != new->panner.canvas_height || cur->simple.margin_width != new->simple.margin_width || cur->simple.margin_height != new->simple.margin_height) { Resize (gnew); /* does a ScaleKnob as well */ redisplay = TRUE; } else { Boolean loc = (cur->panner.slider_x != new->panner.slider_x || cur->panner.slider_y != new->panner.slider_y); Boolean siz = (cur->panner.slider_width != new->panner.slider_width || cur->panner.slider_height != new->panner.slider_height); if (loc || siz) { ScaleKnob (new, loc, siz); redisplay = TRUE; } } return redisplay; } /********************************************************************* * * DrawVisual * an ExmSimple class method * *********************************************************************/ static void DrawVisual( Widget gw) { ExmSimpleWidgetClass wc = (ExmSimpleWidgetClass)XtClass(gw); ExmPannerWidget pw = (ExmPannerWidget) gw; int kx = pw->panner.knob_x + pw->simple.margin_width; int ky = pw->panner.knob_y + pw->simple.margin_height; int lx = pw->panner.last_x + pw->simple.margin_width; int ly = pw->panner.last_y + pw->simple.margin_height; /* Clear rubberband */ if (pw->panner.tmp.doing && pw->panner.rubber_band && pw->panner.tmp.showing) DrawRubberBand (pw); /* Only clear areas that need clearing. This minimizes flickering while dragging. */ if (pw->panner.last_y != PANNER_OUTOFRANGE && pw->panner.last_x != PANNER_OUTOFRANGE) { int height = 0; /* Clear horizontal areas */ if (ly < ky) { /* Clear area above new panner position */ height = ky - ly; XClearArea (XtDisplay(gw), XtWindow(gw), lx, ly, (unsigned int) pw->panner.knob_width, (unsigned int) height, False); } else if (ky < ly) { /* Clear area under new panner */ height = ly - ky; XClearArea (XtDisplay(gw), XtWindow(gw), lx, ky + pw->panner.knob_height - 1, (unsigned int) pw->panner.knob_width, (unsigned int) height, False); } /* Clear vertical areas. Try not to clear the same area twice. */ height = pw->panner.knob_height - height; if (height > 0) { if (lx < kx) { /* Clear area to the left of new panner position */ XClearArea (XtDisplay(gw), XtWindow(gw), lx, ly > ky ? ly : ky, (unsigned int) kx - lx, (unsigned int) height, False); } else if (kx < lx) { /* Clear area to the right of new panner */ XClearArea (XtDisplay(gw), XtWindow(gw), kx + pw->panner.knob_width - 1, ly > ky ? ly : ky, (unsigned int) lx - kx, (unsigned int) height, False); } } } pw->panner.last_x = pw->panner.knob_x; pw->panner.last_y = pw->panner.knob_y; /* use Simple class method to fetch either a normal GC or an insensitive GC. Draw a rectangle inside the shadow. */ XFillRectangle (XtDisplay(gw), XtWindow(gw), wc->simple_class.select_gc(gw), kx + pw->primitive.shadow_thickness, ky + pw->primitive.shadow_thickness, pw->panner.knob_width - 2 * pw->primitive.shadow_thickness - 1, pw->panner.knob_height - 2 * pw->primitive.shadow_thickness - 1); XmeDrawShadows (XtDisplay (gw), XtWindow(gw), pw->primitive.top_shadow_GC, pw->primitive.bottom_shadow_GC, kx, ky, pw->panner.knob_width - 1, pw->panner.knob_height - 1, pw->primitive.shadow_thickness, XmSHADOW_OUT); /* if in the middle of rubber_banding, redisplay it as well */ if (pw->panner.tmp.doing && pw->panner.rubber_band) DrawRubberBand (pw); } /********************************************************************* * * CreateGC * an ExmSimple class method * *********************************************************************/ static void CreateGC( Widget w) { ExmPannerWidget pw = (ExmPannerWidget) w ; XtGCMask valuemask = (GCForeground | GCFunction); XGCValues values; /* Envelop our superclass method to get the normal and the insensitive GC and then add our rubber_band GC */ (*(exmSimpleClassRec.simple_class.create_gc))(w); values.foreground = pw->primitive.foreground ^ pw->core.background_pixel; values.function = GXxor; pw->panner.xor_gc = XtGetGC ((Widget) pw, valuemask, &values); } /********************************************************************* * * DestroyGC * an ExmSimple class method * *********************************************************************/ static void DestroyGC( Widget w) { ExmPannerWidget pw = (ExmPannerWidget) w ; /* Envelop our superclass method to destroy the normal and the insensitive GC, and then release our Xor GC */ (*(exmSimpleClassRec.simple_class.destroy_gc))(w); XtReleaseGC ((Widget) pw, pw->panner.xor_gc); } /********************************************************************* * * DrawRubberBand * Draw the rubberband rectangle when rubberband dragging is used. * *********************************************************************/ static void DrawRubberBand( ExmPannerWidget pw) { XDrawRectangle (XtDisplay(pw), XtWindow(pw), pw->panner.xor_gc, (int) (pw->panner.tmp.x - 1 + pw->simple.margin_width), (int) (pw->panner.tmp.y - 1 + pw->simple.margin_height), (unsigned int) (pw->panner.knob_width), (unsigned int) (pw->panner.knob_height)); pw->panner.tmp.showing = !pw->panner.tmp.showing; } /********************************************************************* * * CheckKnob * Called by several action methods. * *********************************************************************/ static void CheckKnob( register ExmPannerWidget pw, Boolean knob) { Position maxx = (((Position) pw->core.width) - pw->simple.margin_width * 2 - ((Position) pw->panner.knob_width)); Position maxy = (((Position) pw->core.height) - pw->simple.margin_height * 2 - ((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); /* Note that positions are already normalized (i.e. margins have been subtracted out). */ 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; } } /********************************************************************* * * ScaleKnob * Called by Resize and SetValues. This function sets the knob * size and/or its location. * *********************************************************************/ static void ScaleKnob ( ExmPannerWidget pw, Boolean location, Boolean 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 = (pw->panner.slider_width < pw->panner.canvas_width)? pw->panner.slider_width : pw->panner.canvas_width; height = (pw->panner.slider_height < pw->panner.canvas_height)? 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); } CheckKnob (pw, TRUE); } /*************************************************************************** * * * Panner action procs * * * **************************************************************************/ /************************************************************************ * * ExmPannerStart * * ************************************************************************/ static void ExmPannerStart ( Widget gw, XEvent *event, String *params, /* unused */ Cardinal *num_params /* unused */ ) { ExmPannerWidget pw = (ExmPannerWidget) gw; int x, y; if (!GetEventXY (pw, event, &x, &y)) { XBell (XtDisplay(gw), 0); /* should do error message */ return; } if (*num_params != 1) { XBell (XtDisplay(gw), 0); return; } if (XmeNamesAreEqual (params[0], "1")) (void) XmProcessTraversal (gw, XmTRAVERSE_CURRENT); 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 = (((Position) x) - pw->panner.knob_x); pw->panner.tmp.dy = (((Position) 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) DrawRubberBand (pw); } /************************************************************************ * * ExmPannerStop * * ************************************************************************/ static void ExmPannerStop ( Widget gw, XEvent *event, String *params, /* unused */ Cardinal *num_params /* unused */) { ExmPannerWidget pw = (ExmPannerWidget) gw; int x, y; if (GetEventXY (pw, event, &x, &y)) { pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx; pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy; CheckKnob (pw, FALSE); } if (pw->panner.rubber_band && pw->panner.tmp.showing) DrawRubberBand(pw); pw->panner.tmp.doing = FALSE; } /************************************************************************ * * ExmPannerAbort: * Restores slider to the position it had at ExmPannerStart. * ************************************************************************/ static void ExmPannerAbort ( Widget gw, XEvent *event, String *params, /* unused */ Cardinal *num_params /* unused */) { ExmPannerWidget pw = (ExmPannerWidget) gw; if (!pw->panner.tmp.doing) return; if (pw->panner.rubber_band && pw->panner.tmp.showing) DrawRubberBand (pw); if (!pw->panner.rubber_band) { /* restore old position */ pw->panner.tmp.x = pw->panner.tmp.startx; pw->panner.tmp.y = pw->panner.tmp.starty; ExmPannerNotify (gw, event, params, num_params); } pw->panner.tmp.doing = FALSE; } /************************************************************************ * * ExmPannerMove * * ************************************************************************/ static void ExmPannerMove ( Widget gw, XEvent *event, /* must be a motion event */ String *params, /* unused */ Cardinal *num_params /* unused */) { ExmPannerWidget pw = (ExmPannerWidget) gw; int x, y; if (!pw->panner.tmp.doing) return; if (!GetEventXY (pw, event, &x, &y)) { XBell (XtDisplay(gw), 0); /* should do error message */ return; } if (pw->panner.rubber_band && pw->panner.tmp.showing) DrawRubberBand (pw); pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx; pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy; if (!pw->panner.rubber_band) { ExmPannerNotify (gw, event, params, num_params); /* does a check */ } else { CheckKnob (pw, FALSE); DrawRubberBand (pw); } } /************************************************************************ * * ExmPannerNotify * * ************************************************************************/ static void ExmPannerNotify ( Widget gw, XEvent *event, /* unused */ String *params, /* unused */ Cardinal *num_params /* unused */) { ExmSimpleWidgetClass wc = (ExmSimpleWidgetClass)XtClass(gw); ExmPannerWidget pw = (ExmPannerWidget) gw; Position tmp; if (!pw->panner.tmp.doing) return; CheckKnob (pw, FALSE); pw->panner.knob_x = pw->panner.tmp.x; pw->panner.knob_y = pw->panner.tmp.y; 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); /* check knob */ 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; /* call callback if needed */ if (pw->panner.last_x != pw->panner.knob_x || pw->panner.last_y != pw->panner.knob_y) { ExmPannerCallbackStruct rep; wc->core_class.expose (gw, (XEvent*) NULL, (Region) NULL); rep.changed = (ExmPRSliderX | ExmPRSliderY); 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); } } /************************************************************************ * * ExmPannerSet * * ************************************************************************/ static void ExmPannerSet ( Widget gw, XEvent *event, /* unused */ String *params, Cardinal *num_params) { ExmPannerWidget pw = (ExmPannerWidget) gw; Boolean rb; if (*num_params < 2 || !XmeNamesAreEqual (params[0], "rubberband")) { XBell (XtDisplay(gw), 0); return; } if (XmeNamesAreEqual (params[1], "on")) { rb = TRUE; } else if (XmeNamesAreEqual (params[1], "off")) { rb = FALSE; } else if (XmeNamesAreEqual (params[1], "toggle")) { rb = !pw->panner.rubber_band; } else { XBell (XtDisplay(gw), 0); return; } if (rb != pw->panner.rubber_band) { XtVaSetValues (gw, ExmNrubberBand, rb, NULL); } } /************************************************************************ * * GetEventXY * * ************************************************************************/ static Boolean GetEventXY ( ExmPannerWidget pw, XEvent *event, int *x, int *y) { switch (event->type) { case ButtonPress: case ButtonRelease: *x = event->xbutton.x - pw->simple.margin_width; *y = event->xbutton.y - pw->simple.margin_height; return TRUE; case KeyPress: case KeyRelease: *x = event->xkey.x - pw->simple.margin_width; *y = event->xkey.y - pw->simple.margin_height; return TRUE; case EnterNotify: case LeaveNotify: *x = event->xcrossing.x - pw->simple.margin_width; *y = event->xcrossing.y - pw->simple.margin_height; return TRUE; case MotionNotify: *x = event->xmotion.x - pw->simple.margin_width; *y = event->xmotion.y - pw->simple.margin_height; return TRUE; } return FALSE; } /************************************************************************ * * ParsePageString * * ************************************************************************/ static int ParsePageString ( register char *s, int pagesize, int canvassize, Boolean *relative) { char *cp; int val = 1; Boolean rel = FALSE; /* Syntax: spaces [+-] number spaces [pc\0] spaces */ for (; isascii(*s) && isspace(*s); s++) ; /* skip white space */ if (*s == '+' || *s == '-') { /* deal with signs */ rel = TRUE; if (*s == '-') val = -1; s++; } if (!*s) { /* if null then return nothing */ *relative = TRUE; return 0; } /* skip over numbers */ for (cp = s; isascii(*s) && isdigit(*s); s++) ; val *= atoi (cp); /* skip blanks */ for (; isascii(*s) && isspace(*s); s++) ; if (*s) { /* if units */ switch (s[0]) { case 'p': case 'P': val *= pagesize; break; case 'c': case 'C': val *= canvassize; break; } } *relative = rel; return (val); } /************************************************************************ * * ExmPannerPage * * ************************************************************************/ static void ExmPannerPage ( Widget gw, XEvent *event, /* unused */ String *params, Cardinal *num_params /* unused */) { ExmPannerWidget pw = (ExmPannerWidget) gw; Cardinal zero = 0; Boolean isin = pw->panner.tmp.doing; int x, y; Boolean relx, rely; if (*num_params != 2) { XBell (XtDisplay(gw), 0); return; } x = ParsePageString (params[0], (int) pw->panner.knob_width, ((int) pw->core.width) - pw->simple.margin_width*2, &relx); y = ParsePageString (params[1], (int) pw->panner.knob_height, ((int) pw->core.height) - pw->simple.margin_height*2, &rely); if (relx) x += pw->panner.knob_x; if (rely) y += pw->panner.knob_y; if (isin) { /* if in, then use move */ XEvent ev; ev.xbutton.type = ButtonPress; ev.xbutton.x = x; ev.xbutton.y = y; ExmPannerMove (gw, &ev, (String *) NULL, &zero); } else { /* else just do it */ pw->panner.tmp.doing = TRUE; pw->panner.tmp.x = x; pw->panner.tmp.y = y; ExmPannerNotify (gw, event, (String *) NULL, &zero); pw->panner.tmp.doing = FALSE; } } /*************************************************************************** * * * XmQTnavigator Trait Methods * * * **************************************************************************/ /************************************************************************ * * NavigChangeMoveCB * store or remove the callback list to be called on any move. * ************************************************************************/ static void NavigChangeMoveCB( Widget nav, XtCallbackProc moveCB, XtPointer closure, Boolean setunset) { if (setunset) XtAddCallback (nav, ExmNreportCallback, moveCB, closure); else XtRemoveCallback (nav, ExmNreportCallback, moveCB, closure); } /************************************************************************ * * NavigSetValue * change the value and possibly call the callbacks * ************************************************************************/ static void NavigSetValue( Widget nav, XmNavigatorData nav_data, Boolean notify) { ExmPannerWidget pw = (ExmPannerWidget) nav; Arg arglist[6]; Cardinal n ; int hsave_value, vsave_value ; if (nav_data->valueMask & NavDimMask) { pw->panner.dimMask = nav_data->dimMask ; } /* Panner is a two dimensional navigator, it expects one or two dimension to be set, either case should match the ones registered using the NavDimMask flag as aboce. */ if (!(pw->panner.dimMask & nav_data->dimMask)) return ; hsave_value = pw->panner.slider_x ; vsave_value = pw->panner.slider_y ; n = 0; if (nav_data->dimMask & NavigDimensionX) { if ((nav_data->valueMask & NavValue) && (pw->panner.slider_x != nav_data->value.x)) { XtSetArg (arglist[n], ExmNsliderX, nav_data->value.x); n++; } if ((nav_data->valueMask & NavMaximum) && (pw->panner.canvas_width != nav_data->maximum.x)) { XtSetArg (arglist[n], ExmNcanvasWidth,nav_data->maximum.x); n++; } if ((nav_data->valueMask & NavSliderSize) && (pw->panner.slider_width != nav_data->slider_size.x) && (nav_data->slider_size.x != 0)) { XtSetArg (arglist[n], ExmNsliderWidth, nav_data->slider_size.x); n++; } } if (nav_data->dimMask & NavigDimensionY) { if ((nav_data->valueMask & NavValue) && (pw->panner.slider_y != nav_data->value.y)) { XtSetArg (arglist[n], ExmNsliderY, nav_data->value.y); n++; } if ((nav_data->valueMask & NavMaximum) && (pw->panner.canvas_height != nav_data->maximum.y)) { XtSetArg (arglist[n], ExmNcanvasHeight, nav_data->maximum.y);n++; } if ((nav_data->valueMask & NavSliderSize) && (pw->panner.slider_height != nav_data->slider_size.y) && (nav_data->slider_size.y != 0)) { XtSetArg (arglist[n], ExmNsliderHeight, nav_data->slider_size.y); n++; } } if (n) XtSetValues (nav, arglist, n); if (notify && ((pw->panner.slider_x != hsave_value) || (pw->panner.slider_y != vsave_value))) { ExmPannerCallbackStruct rep; rep.changed = (ExmPRSliderX | ExmPRSliderY); /* not always both */ 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 (nav, pw->panner.report_callbacks, (XtPointer) &rep); } } /************************************************************************ * * NavigGetValue * reports the all the data for this navigator scrollbar * ************************************************************************/ static void NavigGetValue( Widget nav, XmNavigatorData nav_data) { ExmPannerWidget pw = (ExmPannerWidget) nav; nav_data->dimMask = pw->panner.dimMask; if (nav_data->valueMask & (NavValue|NavMinimum|NavMaximum| NavSliderSize|NavIncrement|NavPageIncrement)) { nav_data->value.x = pw->panner.slider_x ; nav_data->value.y = pw->panner.slider_y ; nav_data->maximum.x = pw->panner.canvas_width ; nav_data->maximum.y = pw->panner.canvas_height ; nav_data->slider_size.x = pw->panner.slider_width ; nav_data->slider_size.y = pw->panner.slider_height ; } } /************************************************************************ * * Application Accessible External Functions * ************************************************************************/ /************************************************************************ * * ExmCreatePanner * Create an instance of a Panner and return the widget id. * ************************************************************************/ Widget ExmCreatePanner( Widget parent, char *name, ArgList arglist, Cardinal argcount ) { return (XtCreateWidget (name, exmPannerWidgetClass, parent, arglist, argcount)); } /************************************************************************ * * ExmPannerSetValues * Set some Panner values. * ************************************************************************/ void ExmPannerSetValues( Widget w, int hvalue, int vvalue, #if NeedWidePrototypes int notify ) #else Boolean notify ) #endif /* NeedWidePrototypes */ { ExmPannerWidget pw = (ExmPannerWidget) w; int hsave_value, vsave_value; hsave_value = pw->panner.slider_x ; vsave_value = pw->panner.slider_y ; XtVaSetValues (w, ExmNsliderX, hvalue, ExmNsliderY, vvalue, NULL); if (notify && ((pw->panner.slider_x != hsave_value) || (pw->panner.slider_y != vsave_value))) { ExmPannerCallbackStruct rep; rep.changed = (ExmPRSliderX | ExmPRSliderY); /* not always both..*/ 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 (w, pw->panner.report_callbacks, (XtPointer) &rep); } }