Back to index

tetex-bin  3.0
Panner.c
Go to the documentation of this file.
00001 /*
00002  * $XConsortium: Panner.c,v 1.52 95/01/10 14:31:26 kaleb Exp $
00003  *
00004 Copyright (c) 1989, 1994  X Consortium
00005 
00006 Permission is hereby granted, free of charge, to any person obtaining a copy
00007 of this software and associated documentation files (the "Software"), to deal
00008 in the Software without restriction, including without limitation the rights
00009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010 copies of the Software, and to permit persons to whom the Software is
00011 furnished to do so, subject to the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be included in
00014 all copies or substantial portions of the Software.
00015 
00016 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00019 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00020 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00021 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 Except as contained in this notice, the name of the X Consortium shall not be
00024 used in advertising or otherwise to promote the sale, use or other dealings
00025 in this Software without prior written authorization from the X Consortium.
00026  *
00027  * Author:  Jim Fulton, MIT X Consortium
00028  */
00029 
00030 #include "xdvi-config.h"
00031 #include "xdvi.h"
00032 #include "string-utils.h"
00033 
00034 #if defined(MOTIF) && defined(USE_PANNER) && USE_XAW_PANNER /* entire file */
00035 
00036 #if defined(__GNUC__) && DEVEL_MODE
00037 #warning COMPILING Panner_c
00038 #endif
00039 
00040 #include <X11/IntrinsicP.h>
00041 #include <X11/StringDefs.h>        /* for XtN and XtC defines */
00042 #include <X11/Xmu/CharSet.h>              /* for XmuCompareISOLatin1() */
00043 #include "PannerP.h"        /* us */
00044 #include <X11/Xos.h>
00045 #include <X11/Xmu/Misc.h>          /* for Min */
00046 #include <X11/Xmu/Drawing.h>
00047 #include <ctype.h>                 /* for isascii() etc. */
00048 
00049 extern Bool XmuDistinguishablePixels(); /* not defined in any Xmu headers */
00050 
00051 /*
00052   ======================================================================
00053   begin copy from Simple.c
00054   ======================================================================
00055 */
00056 
00057 #define offset(field) XtOffsetOf(SimpleRec, simple.field)
00058 
00059 static XtResource resources[] = {
00060   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
00061      offset(cursor), XtRImmediate, (XtPointer) None},
00062   {XtNinsensitiveBorder, XtCInsensitive, XtRPixmap, sizeof(Pixmap),
00063      offset(insensitive_border), XtRImmediate, (XtPointer) NULL},
00064   {XtNpointerColor, XtCForeground, XtRPixel, sizeof(Pixel),
00065      offset(pointer_fg), XtRString, XtDefaultForeground},
00066   {XtNpointerColorBackground, XtCBackground, XtRPixel, sizeof(Pixel),
00067      offset(pointer_bg), XtRString, XtDefaultBackground},
00068   {XtNcursorName, XtCCursor, XtRString, sizeof(String),
00069      offset(cursor_name), XtRString, NULL},
00070   {XtNinternational, XtCInternational, XtRBoolean, sizeof(Boolean),
00071      offset(international), XtRImmediate, (XtPointer) FALSE},
00072 #undef offset
00073 };
00074 
00075 static void ClassPartInitialize(), ClassInitialize(),Realize(),ConvertCursor();
00076 static Bool SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args);
00077 static Bool ChangeSensitive(Widget w);
00078 
00079 SimpleClassRec simpleClassRec = {
00080   { /* core fields */
00081     /* superclass           */     (WidgetClass) &widgetClassRec,
00082     /* class_name           */     "Simple",
00083     /* widget_size          */     sizeof(SimpleRec),
00084     /* class_initialize            */     ClassInitialize,
00085     /* class_part_initialize       */     ClassPartInitialize,
00086     /* class_inited         */     FALSE,
00087     /* initialize           */     NULL,
00088     /* initialize_hook             */     NULL,
00089     /* realize                     */     Realize,
00090     /* actions                     */     NULL,
00091     /* num_actions          */     0,
00092     /* resources            */     resources,
00093     /* num_resources        */     XtNumber(resources),
00094     /* xrm_class            */     NULLQUARK,
00095     /* compress_motion             */     TRUE,
00096     /* compress_exposure    */     TRUE,
00097     /* compress_enterleave  */     TRUE,
00098     /* visible_interest            */     FALSE,
00099     /* destroy                     */     NULL,
00100     /* resize               */     NULL,
00101     /* expose               */     NULL,
00102 #warning FIXME: incompatible pointer type    
00103     /* set_values           */     SetValues,
00104     /* set_values_hook             */     NULL,
00105     /* set_values_almost    */     XtInheritSetValuesAlmost,
00106     /* get_values_hook             */     NULL,
00107     /* accept_focus         */     NULL,
00108     /* version                     */     XtVersion,
00109     /* callback_private            */     NULL,
00110     /* tm_table                    */     NULL,
00111     /* query_geometry              */     XtInheritQueryGeometry,
00112     /* display_accelerator  */     XtInheritDisplayAccelerator,
00113     /* extension            */     NULL
00114   },
00115   { /* simple fields */
00116     /* change_sensitive            */     ChangeSensitive
00117 #ifndef HAVE_OLD_XAW
00118     , NULL
00119 #endif
00120   }
00121 };
00122 
00123 WidgetClass simpleWidgetClass = (WidgetClass)&simpleClassRec;
00124 
00125 static void ClassInitialize()
00126 {
00127     static XtConvertArgRec convertArg[] = {
00128         {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.screen),
00129             sizeof(Screen *)},
00130         {XtResourceString, (XtPointer) XtNpointerColor, sizeof(Pixel)},
00131         {XtResourceString, (XtPointer) XtNpointerColorBackground, 
00132             sizeof(Pixel)},
00133         {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.colormap),
00134             sizeof(Colormap)}
00135     };
00136 
00137     XawInitializeWidgetSet();
00138     XtSetTypeConverter( XtRString, XtRColorCursor, XmuCvtStringToColorCursor,
00139                      convertArg, XtNumber(convertArg), 
00140                      XtCacheByDisplay, (XtDestructor)NULL);
00141 }
00142 
00143 static void ClassPartInitialize(class)
00144     WidgetClass class;
00145 {
00146     SimpleWidgetClass c     = (SimpleWidgetClass) class;
00147     SimpleWidgetClass super = (SimpleWidgetClass)
00148       c->core_class.superclass;
00149 
00150     if (c->simple_class.change_sensitive == NULL) {
00151        char buf[BUFSIZ];
00152 
00153        (void) sprintf(buf,
00154               "%s Widget: The Simple Widget class method 'change_sensitive' is undefined.\nA function must be defined or inherited.",
00155               c->core_class.class_name);
00156        XtWarning(buf);
00157        c->simple_class.change_sensitive = ChangeSensitive;
00158     }
00159 
00160     if (c->simple_class.change_sensitive == XtInheritChangeSensitive)
00161        c->simple_class.change_sensitive = super->simple_class.change_sensitive;
00162 }
00163 
00164 static void Realize(w, valueMask, attributes)
00165     Widget w;
00166     Mask *valueMask;
00167     XSetWindowAttributes *attributes;
00168 {
00169     Pixmap border_pixmap = 0;
00170     if (!XtIsSensitive(w)) {
00171        /* change border to gray; have to remember the old one,
00172         * so XtDestroyWidget deletes the proper one */
00173        if (((SimpleWidget)w)->simple.insensitive_border == None)
00174            ((SimpleWidget)w)->simple.insensitive_border =
00175               XmuCreateStippledPixmap(XtScreen(w),
00176                                    w->core.border_pixel, 
00177                                    w->core.background_pixel,
00178                                    w->core.depth);
00179         border_pixmap = w->core.border_pixmap;
00180        attributes->border_pixmap =
00181          w->core.border_pixmap = ((SimpleWidget)w)->simple.insensitive_border;
00182 
00183        *valueMask |= CWBorderPixmap;
00184        *valueMask &= ~CWBorderPixel;
00185     }
00186 
00187     ConvertCursor(w);
00188 
00189     if ((attributes->cursor = ((SimpleWidget)w)->simple.cursor) != None)
00190        *valueMask |= CWCursor;
00191 
00192     XtCreateWindow( w, (unsigned int)InputOutput, (Visual *)CopyFromParent,
00193                   *valueMask, attributes );
00194 
00195     if (!XtIsSensitive(w))
00196        w->core.border_pixmap = border_pixmap;
00197 }
00198 
00199 /*     Function Name: ConvertCursor
00200  *     Description: Converts a name to a new cursor.
00201  *     Arguments: w - the simple widget.
00202  *     Returns: none.
00203  */
00204 
00205 static void
00206 ConvertCursor(w)
00207 Widget w;
00208 {
00209     SimpleWidget simple = (SimpleWidget) w;
00210     XrmValue from, to;
00211     Cursor cursor;
00212    
00213     if (simple->simple.cursor_name == NULL)
00214        return;
00215 
00216     from.addr = (XPointer) simple->simple.cursor_name;
00217     from.size = strlen((char *) from.addr) + 1;
00218 
00219     to.size = sizeof(Cursor);
00220     to.addr = (XPointer) &cursor;
00221 
00222     if (XtConvertAndStore(w, XtRString, &from, XtRColorCursor, &to)) {
00223        if ( cursor !=  None) 
00224            simple->simple.cursor = cursor;
00225     } 
00226     else {
00227        XtAppErrorMsg(XtWidgetToApplicationContext(w),
00228                     "convertFailed","ConvertCursor","XawError",
00229                     "Simple: ConvertCursor failed.",
00230                     (String *)NULL, (Cardinal *)NULL);
00231     }
00232 }
00233 
00234 
00235 /* ARGSUSED */
00236 static Bool
00237 SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args)
00238 {
00239     SimpleWidget s_old = (SimpleWidget) current;
00240     SimpleWidget s_new = (SimpleWidget) new;
00241     Boolean new_cursor = FALSE;
00242 
00243     UNUSED(request);
00244     UNUSED(args);
00245     UNUSED(num_args);
00246     /* this disables user changes after creation*/
00247     s_new->simple.international = s_old->simple.international;
00248 
00249     if ( XtIsSensitive(current) != XtIsSensitive(new) )
00250        (*((SimpleWidgetClass)XtClass(new))->
00251             simple_class.change_sensitive) ( new );
00252 
00253     if (s_old->simple.cursor != s_new->simple.cursor) {
00254        new_cursor = TRUE;
00255     }
00256        
00257 /*
00258  * We are not handling the string cursor_name correctly here.
00259  */
00260 
00261     if ( (s_old->simple.pointer_fg != s_new->simple.pointer_fg) ||
00262        (s_old->simple.pointer_bg != s_new->simple.pointer_bg) ||
00263        (s_old->simple.cursor_name != s_new->simple.cursor_name) ) {
00264        ConvertCursor(new);
00265        new_cursor = TRUE;
00266     }
00267 
00268     if (new_cursor && XtIsRealized(new))
00269         XDefineCursor(XtDisplay(new), XtWindow(new), s_new->simple.cursor);
00270 
00271     return False;   
00272 }
00273 
00274 
00275 static Bool ChangeSensitive(Widget w)
00276 {
00277     if (XtIsRealized(w)) {
00278        if (XtIsSensitive(w))
00279            if (w->core.border_pixmap != XtUnspecifiedPixmap)
00280               XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w),
00281                                     w->core.border_pixmap );
00282            else
00283               XSetWindowBorder( XtDisplay(w), XtWindow(w), 
00284                               w->core.border_pixel );
00285        else {
00286            if (((SimpleWidget)w)->simple.insensitive_border == None)
00287               ((SimpleWidget)w)->simple.insensitive_border =
00288                   XmuCreateStippledPixmap(XtScreen(w),
00289                                        w->core.border_pixel, 
00290                                        w->core.background_pixel,
00291                                        w->core.depth);
00292            XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w),
00293                                 ((SimpleWidget)w)->
00294                                     simple.insensitive_border );
00295        }
00296     }
00297     return False;
00298 }
00299 
00300 /*
00301   ======================================================================
00302   end copy from Simple.c
00303   ======================================================================
00304 */
00305 
00306 
00307 /* following function copied from XawInit.c */
00308 void XawInitializeWidgetSet ()
00309 {
00310     static int firsttime = 1;
00311 
00312     if (firsttime) {
00313        firsttime = 0;
00314        XtInitializeWidgetClass (vendorShellWidgetClass);
00315     }
00316 }
00317 
00318 static char defaultTranslations[] = 
00319   "<Btn1Down>:    start() \n\
00320    <Btn1Motion>:  move() \n\
00321    <Btn1Up>:      notify() stop() \n\
00322    <Btn2Down>:    abort() \n\
00323    :<Key>KP_Enter: set(rubberband,toggle) \n\
00324    <Key>space:    page(+1p,+1p) \n\
00325    <Key>Delete:   page(-1p,-1p) \n\
00326    :<Key>KP_Delete: page(-1p,-1p) \n\
00327    <Key>BackSpace: page(-1p,-1p) \n\
00328    <Key>Left:     page(-.5p,+0) \n\
00329    :<Key>KP_Left:  page(-.5p,+0) \n\
00330    <Key>Right:    page(+.5p,+0) \n\
00331    :<Key>KP_Right: page(+.5p,+0) \n\
00332    <Key>Up:       page(+0,-.5p) \n\
00333    :<Key>KP_Up:    page(+0,-.5p) \n\
00334    <Key>Down:     page(+0,+.5p) \n\
00335    :<Key>KP_Down:  page(+0,+.5p) \n\
00336    <Key>Home:     page(0,0) \n\
00337    :<Key>KP_Home:  page(0,0)";
00338 
00339 
00340 static void ActionStart(), ActionStop(), ActionAbort(), ActionMove();
00341 static void ActionPage(), ActionNotify(), ActionSet();
00342 
00343 static XtActionsRec actions[] = {
00344     { "start", ActionStart },             /* start tmp graphics */
00345     { "stop", ActionStop },        /* stop tmp graphics */
00346     { "abort", ActionAbort },             /* punt */
00347     { "move", ActionMove },        /* move tmp graphics on Motion event */
00348     { "page", ActionPage },        /* page around usually from keyboard */
00349     { "notify", ActionNotify },           /* callback new position */
00350     { "set", ActionSet },          /* set various parameters */
00351 };
00352 
00353 
00354 /*
00355  * resources for the panner
00356  */
00357 static XtResource panner_resources[] = {
00358 #define poff(field) XtOffsetOf(PannerRec, panner.field)
00359     { XtNallowOff, XtCAllowOff, XtRBoolean, sizeof(Boolean),
00360        poff(allow_off), XtRImmediate, (XtPointer) FALSE },
00361     { XtNresize, XtCResize, XtRBoolean, sizeof(Boolean),
00362        poff(resize_to_pref), XtRImmediate, (XtPointer) TRUE },
00363     { XtNreportCallback, XtCReportCallback, XtRCallback, sizeof(XtPointer),
00364        poff(report_callbacks), XtRCallback, (XtPointer) NULL },
00365     { XtNdefaultScale, XtCDefaultScale, XtRDimension, sizeof(Dimension),
00366        poff(default_scale), XtRImmediate, (XtPointer) PANNER_DEFAULT_SCALE },
00367     { XtNrubberBand, XtCRubberBand, XtRBoolean, sizeof(Boolean),
00368        poff(rubber_band), XtRImmediate, (XtPointer) FALSE },
00369     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), 
00370        poff(foreground), XtRString, (XtPointer) XtDefaultBackground },
00371     { XtNinternalSpace, XtCInternalSpace, XtRDimension, sizeof(Dimension),
00372        poff(internal_border), XtRImmediate, (XtPointer) 4 },
00373     { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof(Dimension),
00374        poff(line_width), XtRImmediate, (XtPointer) 0 },
00375     { XtNcanvasWidth, XtCCanvasWidth, XtRDimension, sizeof(Dimension),
00376        poff(canvas_width), XtRImmediate, (XtPointer) 0 },
00377     { XtNcanvasHeight, XtCCanvasHeight, XtRDimension, sizeof(Dimension),
00378        poff(canvas_height), XtRImmediate, (XtPointer) 0 },
00379     { XtNsliderX, XtCSliderX, XtRPosition, sizeof(Position),
00380        poff(slider_x), XtRImmediate, (XtPointer) 0 },
00381     { XtNsliderY, XtCSliderY, XtRPosition, sizeof(Position),
00382        poff(slider_y), XtRImmediate, (XtPointer) 0 },
00383     { XtNsliderWidth, XtCSliderWidth, XtRDimension, sizeof(Dimension),
00384        poff(slider_width), XtRImmediate, (XtPointer) 0 },
00385     { XtNsliderHeight, XtCSliderHeight, XtRDimension, sizeof(Dimension),
00386        poff(slider_height), XtRImmediate, (XtPointer) 0 },
00387     { XtNshadowColor, XtCShadowColor, XtRPixel, sizeof(Pixel),
00388        poff(shadow_color), XtRString, (XtPointer) XtDefaultForeground },
00389     { XtNshadowThickness, XtCShadowThickness, XtRDimension, sizeof(Dimension),
00390        poff(shadow_thickness), XtRImmediate, (XtPointer) 2 },
00391     { XtNbackgroundStipple, XtCBackgroundStipple, XtRString, sizeof(String),
00392        poff(stipple_name), XtRImmediate, (XtPointer) NULL },
00393 #undef poff
00394 };
00395 
00396 
00397 /*
00398  * widget class methods used below
00399  */
00400 static void Initialize();          /* create gc's */
00401 static void PannerRealize();                     /* create window */
00402 static void Destroy();                    /* clean up widget */
00403 static void Resize();                     /* need to rescale ourselves */
00404 static void Redisplay();           /* draw ourselves */
00405 static Boolean PannerSetValues();         /* set all of the resources */
00406 static void SetValuesAlmost();            /* deal with failed setval geom req */
00407 static XtGeometryResult QueryGeometry();  /* say how big we would like to be */
00408 
00409 PannerClassRec pannerClassRec = {
00410   { /* core fields */
00411     /* superclass           */     (WidgetClass) &simpleClassRec,
00412     /* class_name           */     "Panner",
00413     /* widget_size          */     sizeof(PannerRec),
00414     /* class_initialize            */     XawInitializeWidgetSet,
00415     /* class_part_initialize       */     NULL,
00416     /* class_inited         */     FALSE,
00417     /* initialize           */     Initialize,
00418     /* initialize_hook             */     NULL,
00419     /* realize                     */     PannerRealize,
00420     /* actions                     */     actions,
00421     /* num_actions          */     XtNumber(actions),
00422     /* resources            */     panner_resources,
00423     /* num_resources        */     XtNumber(panner_resources),
00424     /* xrm_class            */     NULLQUARK,
00425     /* compress_motion             */     TRUE,
00426     /* compress_exposure    */     TRUE,
00427     /* compress_enterleave  */     TRUE,
00428     /* visible_interest            */     FALSE,
00429     /* destroy                     */     Destroy,
00430     /* resize               */     Resize,
00431     /* expose               */     Redisplay,
00432     /* set_values           */     PannerSetValues,
00433     /* set_values_hook             */     NULL,
00434     /* set_values_almost    */     SetValuesAlmost,
00435     /* get_values_hook             */     NULL,
00436     /* accept_focus         */     NULL,
00437     /* version                     */     XtVersion,
00438     /* callback_private            */     NULL,
00439     /* tm_table                    */     defaultTranslations,
00440     /* query_geometry              */     QueryGeometry,
00441     /* display_accelerator  */     XtInheritDisplayAccelerator,
00442     /* extension            */     NULL
00443   },
00444   { /* simple fields */
00445     /* change_sensitive            */     XtInheritChangeSensitive
00446 #ifndef HAVE_OLD_XAW
00447     , NULL
00448 #endif
00449   },
00450   { /* panner fields */
00451     /* ignore                   */ 0
00452   }
00453 };
00454 
00455 WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec;
00456 
00457 
00458 /*****************************************************************************
00459  *                                                                           *
00460  *                       panner utility routines                          *
00461  *                                                                           *
00462  *****************************************************************************/
00463 
00464 static void reset_shadow_gc (pw)   /* used when resources change */
00465     PannerWidget pw;
00466 {
00467     XtGCMask valuemask = GCForeground;
00468     XGCValues values;
00469     unsigned long   pixels[3];
00470 
00471     if (pw->panner.shadow_gc) XtReleaseGC ((Widget) pw, pw->panner.shadow_gc);
00472 
00473     pixels[0] = pw->panner.foreground;
00474     pixels[1] = pw->core.background_pixel;
00475     pixels[2] = pw->panner.shadow_color;
00476     if (!pw->panner.stipple_name &&
00477        !XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
00478                                 pixels, 3) &&
00479        XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
00480                                 pixels, 2))
00481     {
00482        valuemask = GCTile | GCFillStyle;
00483        values.fill_style = FillTiled;
00484        values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw),
00485                                          pw->panner.foreground,
00486                                          pw->core.background_pixel,
00487                                          pw->core.depth);
00488     }
00489     else
00490     {
00491        if (!pw->panner.line_width &&
00492            !XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
00493                                    pixels, 2))
00494            pw->panner.line_width = 1;
00495        valuemask = GCForeground;
00496        values.foreground = pw->panner.shadow_color;
00497     }
00498     if (pw->panner.line_width > 0) {
00499        values.line_width = pw->panner.line_width;
00500        valuemask |= GCLineWidth;
00501     }
00502 
00503     pw->panner.shadow_gc = XtGetGC ((Widget) pw, valuemask, &values);
00504 }
00505 
00506 static void reset_slider_gc (pw)   /* used when resources change */
00507     PannerWidget pw;
00508 {
00509     XtGCMask valuemask = GCForeground;
00510     XGCValues values;
00511 
00512     if (pw->panner.slider_gc) XtReleaseGC ((Widget) pw, pw->panner.slider_gc);
00513 
00514     values.foreground = pw->panner.foreground;
00515 
00516     pw->panner.slider_gc = XtGetGC ((Widget) pw, valuemask, &values);
00517 }
00518 
00519 static void reset_xor_gc (pw)             /* used when resources change */
00520     PannerWidget pw;
00521 {
00522     if (pw->panner.xor_gc) XtReleaseGC ((Widget) pw, pw->panner.xor_gc);
00523 
00524     if (pw->panner.rubber_band) {
00525        XtGCMask valuemask = (GCForeground | GCFunction);
00526        XGCValues values;
00527        Pixel tmp;
00528 
00529        tmp = ((pw->panner.foreground == pw->core.background_pixel) ?
00530               pw->panner.shadow_color : pw->panner.foreground);
00531        values.foreground = tmp ^ pw->core.background_pixel;
00532        values.function = GXxor;
00533        if (pw->panner.line_width > 0) {
00534            valuemask |= GCLineWidth;
00535            values.line_width = pw->panner.line_width;
00536        }
00537        pw->panner.xor_gc = XtGetGC ((Widget) pw, valuemask, &values);
00538     } else {
00539        pw->panner.xor_gc = NULL;
00540     }
00541 }
00542 
00543 
00544 static void check_knob (pw, knob)
00545     PannerWidget pw;
00546     Boolean knob;
00547 {
00548     Position pad = pw->panner.internal_border * 2;
00549     Position maxx = (((Position) pw->core.width) - pad -
00550                    ((Position) pw->panner.knob_width));
00551     Position maxy = (((Position) pw->core.height) - pad -
00552                    ((Position) pw->panner.knob_height));
00553     Position *x = (knob ? &pw->panner.knob_x : &pw->panner.tmp.x);
00554     Position *y = (knob ? &pw->panner.knob_y : &pw->panner.tmp.y);
00555 
00556     /*
00557      * note that positions are already normalized (i.e. internal_border
00558      * has been subtracted out)
00559      */
00560     if (*x < 0) *x = 0;
00561     if (*x > maxx) *x = maxx;
00562 
00563     if (*y < 0) *y = 0;
00564     if (*y > maxy) *y = maxy;
00565 
00566     if (knob) {
00567        pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) /
00568                                      pw->panner.haspect + 0.5);
00569        pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) /
00570                                      pw->panner.vaspect + 0.5);
00571        pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE;
00572     }
00573 }
00574 
00575 
00576 static void move_shadow (pw)
00577     PannerWidget pw;
00578 {
00579     if (pw->panner.shadow_thickness > 0) {
00580        int lw = pw->panner.shadow_thickness + pw->panner.line_width * 2;
00581        int pad = pw->panner.internal_border;
00582 
00583        if ((int)pw->panner.knob_height > lw && (int)pw->panner.knob_width > lw) {
00584            XRectangle *r = pw->panner.shadow_rects;
00585            r->x = (short) (pw->panner.knob_x + pad + pw->panner.knob_width);
00586            r->y = (short) (pw->panner.knob_y + pad + lw);
00587            r->width = pw->panner.shadow_thickness;
00588            r->height = (unsigned short) (pw->panner.knob_height - lw);
00589            r++;
00590            r->x = (short) (pw->panner.knob_x + pad + lw);
00591            r->y = (short) (pw->panner.knob_y + pad + pw->panner.knob_height);
00592            r->width = (unsigned short) (pw->panner.knob_width - lw +
00593                                     pw->panner.shadow_thickness);
00594            r->height = pw->panner.shadow_thickness;
00595            pw->panner.shadow_valid = TRUE;
00596            return;
00597        }
00598     }
00599     pw->panner.shadow_valid = FALSE;
00600 }
00601 
00602 static void scale_knob (pw, location, size)  /* set knob size and/or loc */
00603     PannerWidget pw;
00604     Boolean location, size;
00605 {
00606     if (location) {
00607        pw->panner.knob_x = (Position) PANNER_HSCALE (pw, pw->panner.slider_x);
00608        pw->panner.knob_y = (Position) PANNER_VSCALE (pw, pw->panner.slider_y);
00609     }
00610     if (size) {
00611        Dimension width, height;
00612 
00613        if (pw->panner.slider_width < 1) {
00614            pw->panner.slider_width = pw->panner.canvas_width;
00615        }
00616        if (pw->panner.slider_height < 1) {
00617            pw->panner.slider_height = pw->panner.canvas_height;
00618        }
00619        width = Min (pw->panner.slider_width, pw->panner.canvas_width);
00620        height = Min (pw->panner.slider_height, pw->panner.canvas_height);
00621 
00622        pw->panner.knob_width = (Dimension) PANNER_HSCALE (pw, width);
00623        pw->panner.knob_height = (Dimension) PANNER_VSCALE (pw, height);
00624     }
00625     if (!pw->panner.allow_off) check_knob (pw, TRUE);
00626     move_shadow (pw);
00627 }
00628 
00629 static void rescale (pw)
00630     PannerWidget pw;
00631 {
00632     int hpad = pw->panner.internal_border * 2;
00633     int vpad = hpad;
00634 
00635     if (pw->panner.canvas_width < 1)
00636       pw->panner.canvas_width = pw->core.width;
00637     if (pw->panner.canvas_height < 1)
00638       pw->panner.canvas_height = pw->core.height;
00639 
00640     if ((int)pw->core.width <= hpad) hpad = 0;
00641     if ((int)pw->core.height <= vpad) vpad = 0;
00642 
00643     pw->panner.haspect = ((double) pw->core.width - hpad) /
00644                        (double) pw->panner.canvas_width;
00645     pw->panner.vaspect = ((double) pw->core.height - vpad) /
00646                        (double) pw->panner.canvas_height;
00647     scale_knob (pw, TRUE, TRUE);
00648 }
00649 
00650 
00651 static void get_default_size (pw, wp, hp)
00652     PannerWidget pw;
00653     Dimension *wp, *hp;
00654 {
00655     Dimension pad = pw->panner.internal_border * 2;
00656     *wp = PANNER_DSCALE (pw, pw->panner.canvas_width) + pad;
00657     *hp = PANNER_DSCALE (pw, pw->panner.canvas_height) + pad;
00658 }
00659 
00660 static Boolean get_event_xy (pw, event, x, y)
00661     PannerWidget pw;
00662     XEvent *event;
00663     int *x, *y;
00664 {
00665     int pad = pw->panner.internal_border;
00666 
00667     switch (event->type) {
00668       case ButtonPress:
00669       case ButtonRelease:
00670        *x = event->xbutton.x - pad;
00671        *y = event->xbutton.y - pad;
00672        return TRUE;
00673 
00674       case KeyPress:
00675       case KeyRelease:
00676        *x = event->xkey.x - pad;
00677        *y = event->xkey.y - pad;
00678        return TRUE;
00679 
00680       case EnterNotify:
00681       case LeaveNotify:
00682        *x = event->xcrossing.x - pad;
00683        *y = event->xcrossing.y - pad;
00684        return TRUE;
00685 
00686       case MotionNotify:
00687        *x = event->xmotion.x - pad;
00688        *y = event->xmotion.y - pad;
00689        return TRUE;
00690     }
00691 
00692     return FALSE;
00693 }
00694 
00695 static int parse_page_string (s, pagesize, canvassize, relative)
00696     char *s;
00697     int pagesize, canvassize;
00698     Boolean *relative;
00699 {
00700     char *cp;
00701     double val = 1.0;
00702     Boolean rel = FALSE;
00703 
00704     /*
00705      * syntax:    spaces [+-] number spaces [pc\0] spaces
00706      */
00707 
00708     for (; isascii(*s) && isspace(*s); s++) ;    /* skip white space */
00709 
00710     if (*s == '+' || *s == '-') {  /* deal with signs */
00711        rel = TRUE;
00712        if (*s == '-') val = -1.0;
00713        s++;
00714     }
00715     if (!*s) {                            /* if null then return nothing */
00716        *relative = TRUE;
00717        return 0;
00718     }
00719 
00720                                    /* skip over numbers */
00721     for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++) ;
00722     val *= atof(cp);
00723 
00724                                    /* skip blanks */
00725     for (; isascii(*s) && isspace(*s); s++) ;
00726 
00727     if (*s) {                      /* if units */
00728        switch (s[0]) {
00729          case 'p': case 'P':
00730            val *= (double) pagesize;
00731            break;
00732 
00733          case 'c': case 'C':
00734            val *= (double) canvassize;
00735            break;
00736        }
00737     }
00738     *relative = rel;
00739     return ((int) val);
00740 }
00741 
00742 
00743 #define DRAW_TMP(pw) \
00744 { \
00745     XDrawRectangle (XtDisplay(pw), XtWindow(pw), \
00746                   pw->panner.xor_gc, \
00747                   (int) (pw->panner.tmp.x + pw->panner.internal_border), \
00748                   (int) (pw->panner.tmp.y + pw->panner.internal_border), \
00749                   (unsigned int) (pw->panner.knob_width - 1), \
00750                   (unsigned int) (pw->panner.knob_height - 1)); \
00751     pw->panner.tmp.showing = !pw->panner.tmp.showing; \
00752 }
00753 
00754 #define UNDRAW_TMP(pw) \
00755 { \
00756     if (pw->panner.tmp.showing) DRAW_TMP(pw); \
00757 }
00758 
00759 #define BACKGROUND_STIPPLE(pw) \
00760   XmuLocatePixmapFile (pw->core.screen, pw->panner.stipple_name, \
00761                      pw->panner.shadow_color, pw->core.background_pixel, \
00762                      pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL)
00763     
00764 #define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap)
00765 
00766 
00767 /*****************************************************************************
00768  *                                                                           *
00769  *                        panner class methods                            *
00770  *                                                                           *
00771  *****************************************************************************/
00772 
00773 
00774 /*ARGSUSED*/
00775 static void Initialize (greq, gnew, args, num_args)
00776     Widget greq, gnew;
00777     ArgList args;
00778     Cardinal *num_args;
00779 {
00780     PannerWidget req = (PannerWidget) greq, new = (PannerWidget) gnew;
00781     Dimension defwidth, defheight;
00782 
00783     UNUSED(args);
00784     UNUSED(num_args);
00785 
00786     if (req->panner.canvas_width < 1) new->panner.canvas_width = 1;
00787     if (req->panner.canvas_height < 1) new->panner.canvas_height = 1;
00788     if (req->panner.default_scale < 1)
00789       new->panner.default_scale = PANNER_DEFAULT_SCALE;
00790 
00791     get_default_size (req, &defwidth, &defheight);
00792     if (req->core.width < 1) new->core.width = defwidth;
00793     if (req->core.height < 1) new->core.height = defheight;
00794 
00795     new->panner.shadow_gc = NULL;
00796     reset_shadow_gc (new);         /* shadowColor */
00797     new->panner.slider_gc = NULL;
00798     reset_slider_gc (new);         /* foreground */
00799     new->panner.xor_gc = NULL;
00800     reset_xor_gc (new);                   /* foreground ^ background */
00801 
00802     rescale (new);                 /* does a position check */
00803     new->panner.shadow_valid = FALSE;
00804     new->panner.tmp.doing = FALSE;
00805     new->panner.tmp.showing = FALSE;
00806 }
00807 
00808 
00809 static void PannerRealize (gw, valuemaskp, attr)
00810     Widget gw;
00811     XtValueMask *valuemaskp;
00812     XSetWindowAttributes *attr;
00813 {
00814     PannerWidget pw = (PannerWidget) gw;
00815     Pixmap pm = XtUnspecifiedPixmap;
00816     Boolean gotpm = FALSE;
00817 
00818     if (pw->core.background_pixmap == XtUnspecifiedPixmap) {
00819        if (pw->panner.stipple_name) pm = BACKGROUND_STIPPLE (pw);
00820 
00821        if (PIXMAP_OKAY(pm)) {
00822            attr->background_pixmap = pm;
00823            *valuemaskp |= CWBackPixmap;
00824            *valuemaskp &= ~CWBackPixel;
00825            gotpm = TRUE;
00826        }
00827     }
00828     (*pannerWidgetClass->core_class.superclass->core_class.realize)
00829       (gw, valuemaskp, attr);
00830 
00831     if (gotpm) XFreePixmap (XtDisplay(gw), pm);
00832 }
00833 
00834 
00835 static void Destroy (gw)
00836     Widget gw;
00837 {
00838     PannerWidget pw = (PannerWidget) gw;
00839 
00840     XtReleaseGC (gw, pw->panner.shadow_gc);
00841     XtReleaseGC (gw, pw->panner.slider_gc);
00842     XtReleaseGC (gw, pw->panner.xor_gc);
00843 }
00844 
00845 
00846 static void Resize (gw)
00847     Widget gw;
00848 {
00849     rescale ((PannerWidget) gw);
00850 }
00851 
00852 
00853 /* ARGSUSED */
00854 static void Redisplay (Widget gw, XEvent *event, Region region)
00855 {
00856     PannerWidget pw = (PannerWidget) gw;
00857     Display *dpy = XtDisplay(gw);
00858     Window w = XtWindow(gw);
00859     int pad = pw->panner.internal_border;
00860     Dimension lw = pw->panner.line_width;
00861     Dimension extra = pw->panner.shadow_thickness + lw * 2;
00862     int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad;
00863 
00864     UNUSED(event);
00865     UNUSED(region);
00866     
00867     pw->panner.tmp.showing = FALSE;
00868     XClearArea (XtDisplay(pw), XtWindow(pw), 
00869               (int) pw->panner.last_x - ((int) lw) + pad, 
00870               (int) pw->panner.last_y - ((int) lw) + pad, 
00871               (unsigned int) (pw->panner.knob_width + extra),
00872               (unsigned int) (pw->panner.knob_height + extra),
00873               False);
00874     pw->panner.last_x = pw->panner.knob_x;
00875     pw->panner.last_y = pw->panner.knob_y;
00876 
00877     XFillRectangle (dpy, w, pw->panner.slider_gc, kx, ky,
00878                   pw->panner.knob_width - 1, pw->panner.knob_height - 1);
00879 
00880     if (lw)
00881     {
00882        XDrawRectangle (dpy, w, pw->panner.shadow_gc, kx, ky,
00883                      (unsigned int) (pw->panner.knob_width - 1), 
00884                      (unsigned int) (pw->panner.knob_height - 1));
00885     }
00886 
00887     if (pw->panner.shadow_valid) {
00888        XFillRectangles (dpy, w, pw->panner.shadow_gc,
00889                       pw->panner.shadow_rects, 2);
00890     }
00891     if (pw->panner.tmp.doing && pw->panner.rubber_band) DRAW_TMP (pw);
00892 }
00893 
00894 
00895 /* ARGSUSED */
00896 static Boolean PannerSetValues (Widget gcur, Widget greq, Widget gnew, ArgList args, Cardinal *num_args)
00897 {
00898     PannerWidget cur = (PannerWidget) gcur;
00899     PannerWidget new = (PannerWidget) gnew;
00900     Boolean redisplay = FALSE;
00901 
00902     UNUSED(greq);
00903     UNUSED(args);
00904     UNUSED(num_args);
00905     
00906     if (cur->panner.foreground != new->panner.foreground) {
00907        reset_slider_gc (new);
00908        if (cur->panner.foreground != cur->core.background_pixel)
00909          reset_xor_gc (new);
00910        redisplay = TRUE;
00911     } else if (cur->panner.line_width != new->panner.line_width ||
00912               cur->core.background_pixel != new->core.background_pixel) {
00913        reset_xor_gc (new);
00914        redisplay = TRUE;
00915     }
00916     if (cur->panner.shadow_color != new->panner.shadow_color) {
00917        reset_shadow_gc (new);
00918        if (cur->panner.foreground == cur->core.background_pixel)
00919          reset_xor_gc (new);
00920        redisplay = TRUE;
00921     }
00922     if (cur->panner.shadow_thickness != new->panner.shadow_thickness) {
00923        move_shadow (new);
00924        redisplay = TRUE;
00925     }
00926     if (cur->panner.rubber_band != new->panner.rubber_band) {
00927        reset_xor_gc (new);
00928        if (new->panner.tmp.doing) redisplay = TRUE;
00929     }
00930 
00931     if ((cur->panner.stipple_name != new->panner.stipple_name ||
00932         cur->panner.shadow_color != new->panner.shadow_color ||
00933         cur->core.background_pixel != new->core.background_pixel) &&
00934        XtIsRealized(gnew)) {
00935        Pixmap pm = (new->panner.stipple_name ? BACKGROUND_STIPPLE (new)
00936                    : XtUnspecifiedPixmap);
00937 
00938        if (PIXMAP_OKAY(pm)) {
00939            XSetWindowBackgroundPixmap (XtDisplay (new), XtWindow(new), pm);
00940            XFreePixmap (XtDisplay (new), pm);
00941        } else {
00942            XSetWindowBackground (XtDisplay (new), XtWindow(new),
00943                               new->core.background_pixel);
00944        }
00945        redisplay = TRUE;
00946     }
00947 
00948     if (new->panner.resize_to_pref &&
00949        (cur->panner.canvas_width != new->panner.canvas_width ||
00950         cur->panner.canvas_height != new->panner.canvas_height ||
00951         cur->panner.resize_to_pref != new->panner.resize_to_pref)) {
00952        get_default_size (new, &new->core.width, &new->core.height);
00953        redisplay = TRUE;
00954     } else if (cur->panner.canvas_width != new->panner.canvas_width ||
00955        cur->panner.canvas_height != new->panner.canvas_height ||
00956        cur->panner.internal_border != new->panner.internal_border) {
00957        rescale (new);                     /* does a scale_knob as well */
00958        redisplay = TRUE;
00959     } else {
00960        Boolean loc = (cur->panner.slider_x != new->panner.slider_x ||
00961                      cur->panner.slider_y != new->panner.slider_y);
00962        Boolean siz = (cur->panner.slider_width != new->panner.slider_width ||
00963                      cur->panner.slider_height != new->panner.slider_height);
00964        if (loc || siz ||
00965            (cur->panner.allow_off != new->panner.allow_off &&
00966             new->panner.allow_off)) {
00967            scale_knob (new, loc, siz);
00968            redisplay = TRUE;
00969        }
00970     }
00971 
00972     return redisplay;
00973 }
00974 
00975 static void SetValuesAlmost (gold, gnew, req, reply)
00976     Widget gold, gnew;
00977     XtWidgetGeometry *req, *reply;
00978 {
00979     if (reply->request_mode == 0) {       /* got turned down, so cope */
00980        Resize (gnew);
00981     }
00982     (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost)
00983        (gold, gnew, req, reply);
00984 }
00985 
00986 static XtGeometryResult QueryGeometry (gw, intended, pref)
00987     Widget gw;
00988     XtWidgetGeometry *intended, *pref;
00989 {
00990     PannerWidget pw = (PannerWidget) gw;
00991 
00992     pref->request_mode = (CWWidth | CWHeight);
00993     get_default_size (pw, &pref->width, &pref->height);
00994 
00995     if (((intended->request_mode & (CWWidth | CWHeight)) ==
00996         (CWWidth | CWHeight)) &&
00997        intended->width == pref->width &&
00998        intended->height == pref->height)
00999       return XtGeometryYes;
01000     else if (pref->width == pw->core.width && pref->height == pw->core.height)
01001       return XtGeometryNo;
01002     else
01003       return XtGeometryAlmost;
01004 }
01005 
01006 
01007 /*****************************************************************************
01008  *                                                                           *
01009  *                         panner action procs                            *
01010  *                                                                           *
01011  *****************************************************************************/
01012 
01013 /* ARGSUSED */
01014 static void ActionStart (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01015 {
01016     PannerWidget pw = (PannerWidget) gw;
01017     int x, y;
01018 
01019     UNUSED(params);
01020     UNUSED(num_params);
01021     
01022     if (!get_event_xy (pw, event, &x, &y)) {
01023        XBell (XtDisplay(gw), 0);   /* should do error message */
01024        return;
01025     }
01026 
01027     pw->panner.tmp.doing = TRUE;
01028     pw->panner.tmp.startx = pw->panner.knob_x;
01029     pw->panner.tmp.starty = pw->panner.knob_y;
01030     pw->panner.tmp.dx = (((Position) x) - pw->panner.knob_x);
01031     pw->panner.tmp.dy = (((Position) y) - pw->panner.knob_y);
01032     pw->panner.tmp.x = pw->panner.knob_x;
01033     pw->panner.tmp.y = pw->panner.knob_y;
01034     if (pw->panner.rubber_band) DRAW_TMP (pw);
01035 }
01036 
01037 /* ARGSUSED */
01038 static void ActionStop (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01039 {
01040     PannerWidget pw = (PannerWidget) gw;
01041     int x, y;
01042 
01043     UNUSED(params);
01044     UNUSED(num_params);
01045     
01046     if (get_event_xy (pw, event, &x, &y)) {
01047        pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx;
01048        pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy;
01049        if (!pw->panner.allow_off) check_knob (pw, FALSE);
01050     }
01051     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
01052     pw->panner.tmp.doing = FALSE;
01053 }
01054 
01055 /* ARGSUSED */
01056 static void ActionAbort (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01057 {
01058     PannerWidget pw = (PannerWidget) gw;
01059 
01060     UNUSED(params);
01061     UNUSED(num_params);
01062     
01063     if (!pw->panner.tmp.doing) return;
01064 
01065     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
01066 
01067     if (!pw->panner.rubber_band) {        /* restore old position */
01068        pw->panner.tmp.x = pw->panner.tmp.startx;
01069        pw->panner.tmp.y = pw->panner.tmp.starty;
01070        ActionNotify (gw, event, params, num_params);
01071     }
01072     pw->panner.tmp.doing = FALSE;
01073 }
01074 
01075 
01076 /* ARGSUSED */
01077 static void ActionMove (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01078 {
01079     PannerWidget pw = (PannerWidget) gw;
01080     int x, y;
01081 
01082     UNUSED(params);
01083     UNUSED(num_params);
01084     
01085     if (!pw->panner.tmp.doing) return;
01086 
01087     if (!get_event_xy (pw, event, &x, &y)) {
01088        XBell (XtDisplay(gw), 0);   /* should do error message */
01089        return;
01090     }
01091 
01092     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
01093     pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx;
01094     pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy;
01095 
01096     if (!pw->panner.rubber_band) {
01097        ActionNotify (gw, event, params, num_params);  /* does a check */
01098     } else {
01099        if (!pw->panner.allow_off) check_knob (pw, FALSE);
01100        DRAW_TMP (pw);
01101     }
01102 }
01103 
01104 
01105 /* ARGSUSED */
01106 static void ActionPage (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01107 {
01108     PannerWidget pw = (PannerWidget) gw;
01109     Cardinal zero = 0;
01110     Boolean isin = pw->panner.tmp.doing;
01111     int x, y;
01112     Boolean relx, rely;
01113     int pad = pw->panner.internal_border * 2;
01114 
01115     UNUSED(event);
01116     UNUSED(num_params);
01117     
01118     if (*num_params != 2) {
01119        XBell (XtDisplay(gw), 0);
01120        return;
01121     }
01122 
01123     x = parse_page_string (params[0], (int) pw->panner.knob_width,
01124                         ((int) pw->core.width) - pad, &relx);
01125     y = parse_page_string (params[1], (int) pw->panner.knob_height,
01126                         ((int) pw->core.height) - pad, &rely);
01127 
01128     if (relx) x += pw->panner.knob_x;
01129     if (rely) y += pw->panner.knob_y;
01130 
01131     if (isin) {                           /* if in, then use move */
01132        XEvent ev;
01133        ev.xbutton.type = ButtonPress;
01134        ev.xbutton.x = x;
01135        ev.xbutton.y = y;
01136        ActionMove (gw, &ev, (String *) NULL, &zero);
01137     } else {                       /* else just do it */
01138        pw->panner.tmp.doing = TRUE;
01139        pw->panner.tmp.x = x;
01140        pw->panner.tmp.y = y;
01141        ActionNotify (gw, event, (String *) NULL, &zero);
01142        pw->panner.tmp.doing = FALSE;
01143     }
01144 }
01145 
01146 
01147 /* ARGSUSED */
01148 static void ActionNotify (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01149 {
01150     PannerWidget pw = (PannerWidget) gw;
01151 
01152     UNUSED(event);
01153     UNUSED(params);
01154     UNUSED(num_params);
01155     
01156     if (!pw->panner.tmp.doing) return;
01157 
01158     if (!pw->panner.allow_off) check_knob (pw, FALSE);
01159     pw->panner.knob_x = pw->panner.tmp.x;
01160     pw->panner.knob_y = pw->panner.tmp.y;
01161     move_shadow (pw);
01162 
01163     pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) /
01164                                   pw->panner.haspect + 0.5);
01165     pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) /
01166                                   pw->panner.vaspect + 0.5);
01167     if (!pw->panner.allow_off) {
01168        Position tmp;
01169        
01170        if (pw->panner.slider_x >
01171            (tmp = (((Position) pw->panner.canvas_width) - 
01172                   ((Position) pw->panner.slider_width))))
01173          pw->panner.slider_x = tmp;
01174        if (pw->panner.slider_x < 0) pw->panner.slider_x = 0;
01175        if (pw->panner.slider_y >
01176            (tmp = (((Position) pw->panner.canvas_height) - 
01177                   ((Position) pw->panner.slider_height))))
01178          pw->panner.slider_y = tmp;
01179        if (pw->panner.slider_y < 0) pw->panner.slider_y = 0;
01180     }
01181 
01182     if (pw->panner.last_x != pw->panner.knob_x ||
01183        pw->panner.last_y != pw->panner.knob_y) {
01184        XawPannerReport rep;
01185 
01186        Redisplay (gw, (XEvent*) NULL, (Region) NULL);
01187        rep.changed = (XawPRSliderX | XawPRSliderY);
01188        rep.slider_x = pw->panner.slider_x;
01189        rep.slider_y = pw->panner.slider_y;
01190        rep.slider_width = pw->panner.slider_width;
01191        rep.slider_height = pw->panner.slider_height;
01192        rep.canvas_width = pw->panner.canvas_width;
01193        rep.canvas_height = pw->panner.canvas_height;
01194        XtCallCallbackList (gw, pw->panner.report_callbacks, (XtPointer) &rep);
01195     }
01196 }
01197 
01198 /* ARGSUSED */
01199 static void ActionSet (Widget gw, XEvent *event, String *params, Cardinal *num_params)
01200 {
01201     PannerWidget pw = (PannerWidget) gw;
01202     Boolean rb;
01203 
01204     UNUSED(event);
01205     
01206     if (*num_params < 2 ||
01207        XmuCompareISOLatin1 (params[0], "rubberband") != 0) {
01208        XBell (XtDisplay(gw), 0);
01209        return;
01210     }
01211 
01212     if (XmuCompareISOLatin1 (params[1], "on") == 0) {
01213        rb = TRUE;
01214     } else if (XmuCompareISOLatin1 (params[1], "off") == 0) {
01215        rb = FALSE;
01216     } else if (XmuCompareISOLatin1 (params[1], "toggle") == 0) {
01217        rb = !pw->panner.rubber_band;
01218     } else {
01219        XBell (XtDisplay(gw), 0);
01220        return;
01221     }
01222 
01223     if (rb != pw->panner.rubber_band) {
01224        Arg args[1];
01225        XtSetArg (args[0], XtNrubberBand, rb);
01226        XtSetValues (gw, args, (Cardinal) 1);
01227     }
01228 }
01229 
01230 #endif /* MOTIF && USE_PANNER */