upload tizen2.0 source
[framework/uifw/xorg/lib/libxaw.git] / src / Panner.c
1 /*
2  *
3 Copyright 1989, 1994, 1998  The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24  *
25  * Author:  Jim Fulton, MIT X Consortium
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <ctype.h>
32 #include <math.h>
33 #include <X11/IntrinsicP.h>
34 #include <X11/StringDefs.h>
35 #include <X11/Xos.h>
36 #include <X11/Xmu/CharSet.h>
37 #include <X11/Xmu/Drawing.h>
38 #include <X11/Xmu/Misc.h>
39 #include <X11/Xaw/PannerP.h>
40 #include <X11/Xaw/XawInit.h>
41 #include "Private.h"
42
43 #if defined(ISC) && __STDC__ && !defined(ISC30)
44 extern double atof(char *);
45 #else
46 #include <stdlib.h>                     /* for atof() */
47 #endif
48
49 /*
50  * Class Methods
51  */
52 static void XawPannerDestroy(Widget);
53 static void XawPannerInitialize(Widget, Widget, ArgList, Cardinal*);
54 static XtGeometryResult XawPannerQueryGeometry(Widget, XtWidgetGeometry*,
55                                                XtWidgetGeometry*);
56 static void XawPannerRealize(Widget, XtValueMask*, XSetWindowAttributes*);
57 static void XawPannerRedisplay(Widget, XEvent*, Region);
58 static void XawPannerResize(Widget);
59 static Boolean XawPannerSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
60 static void XawPannerSetValuesAlmost(Widget, Widget, XtWidgetGeometry*,
61                                      XtWidgetGeometry*);
62
63 /*
64  * Prototypes
65  */
66 static void check_knob(PannerWidget, Bool);
67 static void get_default_size(PannerWidget, Dimension*, Dimension*);
68 static Bool get_event_xy(PannerWidget, XEvent*, int*, int*);
69 static void move_shadow(PannerWidget);
70 static int parse_page_string(char*, int, int, Bool*);
71 static void rescale(PannerWidget);
72 static void reset_shadow_gc(PannerWidget);
73 static void reset_slider_gc(PannerWidget);
74 static void reset_xor_gc(PannerWidget);
75 static void scale_knob(PannerWidget, Bool, Bool);
76
77 /*
78  * Actions
79  */
80 static void ActionAbort(Widget, XEvent*, String*, Cardinal*);
81 static void ActionMove(Widget, XEvent*, String*, Cardinal*);
82 static void ActionNotify(Widget, XEvent*, String*, Cardinal*);
83 static void ActionPage(Widget, XEvent*, String*, Cardinal*);
84 static void ActionSet(Widget, XEvent*, String*, Cardinal*);
85 static void ActionStart(Widget, XEvent*, String*, Cardinal*);
86 static void ActionStop(Widget, XEvent*, String*, Cardinal*);
87
88 /*
89  * From Xmu/Distinct.c
90  */
91 Bool XmuDistinguishablePixels(Display*, Colormap, unsigned long*, int);
92
93 /*
94  * Initialization
95  */
96 static char defaultTranslations[] =
97 "<Btn1Down>:"           "start()\n"
98 "<Btn1Motion>:"         "move()\n"
99 "<Btn1Up>:"             "notify() stop()\n"
100 "<Btn2Down>:"           "abort()\n"
101 ":<Key>KP_Enter:"       "set(rubberband,toggle)\n"
102 "<Key>space:"           "page(+1p,+1p)\n"
103 "<Key>Delete:"          "page(-1p,-1p)\n"
104 ":<Key>KP_Delete:"      "page(-1p,-1p)\n"
105 "<Key>BackSpace:"       "page(-1p,-1p)\n"
106 "<Key>Left:"            "page(-.5p,+0)\n"
107 ":<Key>KP_Left:"        "page(-.5p,+0)\n"
108 "<Key>Right:"           "page(+.5p,+0)\n"
109 ":<Key>KP_Right:"       "page(+.5p,+0)\n"
110 "<Key>Up:"              "page(+0,-.5p)\n"
111 ":<Key>KP_Up:"          "page(+0,-.5p)\n"
112 "<Key>Down:"            "page(+0,+.5p)\n"
113 ":<Key>KP_Down:"        "page(+0,+.5p)\n"
114 "<Key>Home:"            "page(0,0)\n"
115 ":<Key>KP_Home:"        "page(0,0)\n"
116 ;
117
118 static XtActionsRec actions[] = {
119   {"start",     ActionStart},           /* start tmp graphics */
120   {"stop",      ActionStop},            /* stop tmp graphics */
121   {"abort",     ActionAbort},           /* punt */
122   {"move",      ActionMove},            /* move tmp graphics on Motion event */
123   {"page",      ActionPage},            /* page around usually from keyboard */
124   {"notify",    ActionNotify},          /* callback new position */
125   {"set",       ActionSet},             /* set various parameters */
126 };
127
128 #define offset(field)   XtOffsetOf(PannerRec, panner.field)
129 static XtResource resources[] = {
130     {
131       XtNallowOff,
132       XtCAllowOff,
133       XtRBoolean,
134       sizeof(Boolean),
135       offset(allow_off),
136       XtRImmediate,
137       (XtPointer)False
138     },
139     {
140       XtNresize,
141       XtCResize,
142       XtRBoolean,
143       sizeof(Boolean),
144       offset(resize_to_pref),
145       XtRImmediate,
146       (XtPointer)True
147     },
148     {
149       XtNreportCallback,
150       XtCReportCallback,
151       XtRCallback,
152       sizeof(XtPointer),
153       offset(report_callbacks),
154       XtRCallback,
155       NULL
156     },
157     {
158       XtNdefaultScale,
159       XtCDefaultScale,
160       XtRDimension,
161       sizeof(Dimension),
162       offset(default_scale),
163       XtRImmediate,
164       (XtPointer)PANNER_DEFAULT_SCALE
165     },
166     {
167       XtNrubberBand,
168       XtCRubberBand,
169       XtRBoolean,
170       sizeof(Boolean),
171       offset(rubber_band),
172       XtRImmediate,
173       (XtPointer)False
174     },
175     {
176       XtNforeground,
177       XtCForeground,
178       XtRPixel,
179       sizeof(Pixel),
180       offset(foreground),
181       XtRString,
182       (XtPointer)XtDefaultBackground
183     },
184     {
185       XtNinternalSpace,
186       XtCInternalSpace,
187       XtRDimension,
188       sizeof(Dimension),
189       offset(internal_border),
190       XtRImmediate,
191       (XtPointer)4
192     },
193     {
194       XtNlineWidth,
195       XtCLineWidth,
196       XtRDimension,
197       sizeof(Dimension),
198       offset(line_width),
199       XtRImmediate,
200       (XtPointer)0
201     },
202     {
203       XtNcanvasWidth,
204       XtCCanvasWidth,
205       XtRDimension,
206       sizeof(Dimension),
207       offset(canvas_width),
208       XtRImmediate,
209       (XtPointer)0
210     },
211     {
212       XtNcanvasHeight,
213       XtCCanvasHeight,
214       XtRDimension,
215       sizeof(Dimension),
216       offset(canvas_height),
217       XtRImmediate,
218       (XtPointer)0
219     },
220     {
221       XtNsliderX,
222       XtCSliderX,
223       XtRPosition,
224       sizeof(Position),
225       offset(slider_x),
226       XtRImmediate,
227       (XtPointer)0
228     },
229     {
230       XtNsliderY,
231       XtCSliderY,
232       XtRPosition,
233       sizeof(Position),
234       offset(slider_y),
235       XtRImmediate,
236       (XtPointer)0
237     },
238     {
239       XtNsliderWidth,
240       XtCSliderWidth,
241       XtRDimension,
242       sizeof(Dimension),
243       offset(slider_width),
244       XtRImmediate,
245       (XtPointer)0
246     },
247     {
248       XtNsliderHeight,
249       XtCSliderHeight,
250       XtRDimension,
251       sizeof(Dimension),
252       offset(slider_height),
253       XtRImmediate,
254       (XtPointer)0
255     },
256     {
257       XtNshadowColor,
258       XtCShadowColor,
259       XtRPixel,
260       sizeof(Pixel),
261       offset(shadow_color),
262       XtRString,
263       (XtPointer)XtDefaultForeground
264     },
265     {
266       XtNshadowThickness,
267       XtCShadowThickness,
268       XtRDimension,
269       sizeof(Dimension),
270       offset(shadow_thickness),
271       XtRImmediate,
272       (XtPointer)2
273     },
274     {
275       XtNbackgroundStipple,
276       XtCBackgroundStipple,
277       XtRString,
278       sizeof(String),
279       offset(stipple_name),
280       XtRImmediate,
281       NULL
282     },
283 };
284 #undef offset
285
286 #define Superclass      (&simpleClassRec)
287 PannerClassRec pannerClassRec = {
288   /* core */
289   {
290     (WidgetClass)Superclass,            /* superclass */
291     "Panner",                           /* class_name */
292     sizeof(PannerRec),                  /* widget_size */
293     XawInitializeWidgetSet,             /* class_initialize */
294     NULL,                               /* class_part_initialize */
295     False,                              /* class_inited */
296     XawPannerInitialize,                /* initialize */
297     NULL,                               /* initialize_hook */
298     XawPannerRealize,                   /* realize */
299     actions,                            /* actions */
300     XtNumber(actions),                  /* num_actions */
301     resources,                          /* resources */
302     XtNumber(resources),                /* num_resources */
303     NULLQUARK,                          /* xrm_class */
304     True,                               /* compress_motion */
305     True,                               /* compress_exposure */
306     True,                               /* compress_enterleave */
307     False,                              /* visible_interest */
308     XawPannerDestroy,                   /* destroy */
309     XawPannerResize,                    /* resize */
310     XawPannerRedisplay,                 /* expose */
311     XawPannerSetValues,                 /* set_values */
312     NULL,                               /* set_values_hook */
313     XawPannerSetValuesAlmost,           /* set_values_almost */
314     NULL,                               /* get_values_hook */
315     NULL,                               /* accept_focus */
316     XtVersion,                          /* version */
317     NULL,                               /* callback_private */
318     defaultTranslations,                /* tm_table */
319     XawPannerQueryGeometry,             /* query_geometry */
320     XtInheritDisplayAccelerator,        /* display_accelerator */
321     NULL,                               /* extension */
322   },
323   /* simple */
324   {
325     XtInheritChangeSensitive,           /* change_sensitive */
326   },
327   /* panner */
328   {
329     NULL,                               /* extension */
330   }
331 };
332
333 WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec;
334
335
336 /*
337  * Implementation
338  */
339 static void
340 reset_shadow_gc(PannerWidget pw)
341 {
342     XtGCMask valuemask = GCForeground;
343     XGCValues values;
344     unsigned long   pixels[3];
345
346     if (pw->panner.shadow_gc)
347         XtReleaseGC((Widget)pw, pw->panner.shadow_gc);
348
349     pixels[0] = pw->panner.foreground;
350     pixels[1] = pw->core.background_pixel;
351     pixels[2] = pw->panner.shadow_color;
352
353     if (!pw->panner.stipple_name &&
354         !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
355                                   pixels, 3) &&
356         XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
357                                  pixels, 2)) {
358         valuemask = GCTile | GCFillStyle;
359         values.fill_style = FillTiled;
360         values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw),
361                                               pw->panner.foreground,
362                                               pw->core.background_pixel,
363                                               pw->core.depth);
364     }
365     else {
366         if (!pw->panner.line_width &&
367             !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
368                                       pixels, 2))
369             pw->panner.line_width = 1;
370         valuemask = GCForeground;
371         values.foreground = pw->panner.shadow_color;
372     }
373     if (pw->panner.line_width > 0) {
374         values.line_width = pw->panner.line_width;
375         valuemask |= GCLineWidth;
376     }
377
378     pw->panner.shadow_gc = XtGetGC((Widget)pw, valuemask, &values);
379 }
380
381 static void
382 reset_slider_gc(PannerWidget pw)
383 {
384     XtGCMask valuemask = GCForeground;
385     XGCValues values;
386
387     if (pw->panner.slider_gc)
388         XtReleaseGC((Widget)pw, pw->panner.slider_gc);
389
390     values.foreground = pw->panner.foreground;
391
392     pw->panner.slider_gc = XtGetGC((Widget)pw, valuemask, &values);
393 }
394
395 static void
396 reset_xor_gc(PannerWidget pw)
397 {
398     if (pw->panner.xor_gc)
399         XtReleaseGC((Widget)pw, pw->panner.xor_gc);
400
401     if (pw->panner.rubber_band) {
402         XtGCMask valuemask = (GCForeground | GCFunction);
403         XGCValues values;
404         Pixel tmp;
405
406         tmp = (pw->panner.foreground == pw->core.background_pixel ?
407                pw->panner.shadow_color : pw->panner.foreground);
408         values.foreground = tmp ^ pw->core.background_pixel;
409         values.function = GXxor;
410         if (pw->panner.line_width > 0) {
411             valuemask |= GCLineWidth;
412             values.line_width = pw->panner.line_width;
413         }
414         pw->panner.xor_gc = XtGetGC((Widget)pw, valuemask, &values);
415     }
416     else
417         pw->panner.xor_gc = NULL;
418 }
419
420 static void
421 check_knob(PannerWidget pw, Bool knob)
422 {
423     Position pad = pw->panner.internal_border << 1;
424     Position maxx = (Position)XtWidth(pw) - pad -
425                     (Position)pw->panner.knob_width;
426     Position maxy = (Position)XtHeight(pw) - pad -
427                     (Position)pw->panner.knob_height;
428     Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x;
429     Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y;
430
431     /*
432      * note that positions are already normalized (i.e. internal_border
433      * has been subtracted out)
434      */
435     if (*x < 0)
436         *x = 0;
437     if (*x > maxx)
438         *x = maxx;
439
440     if (*y < 0)
441         *y = 0;
442     if (*y > maxy)
443         *y = maxy;
444
445     if (knob) {
446         pw->panner.slider_x = (Position)((double)pw->panner.knob_x
447                                         / pw->panner.haspect + 0.5);
448         pw->panner.slider_y = (Position)((double)pw->panner.knob_y
449                                         / pw->panner.vaspect + 0.5);
450         pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE;
451     }
452 }
453
454 static void
455 move_shadow(PannerWidget pw)
456 {
457     if (pw->panner.shadow_thickness > 0) {
458         int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1);
459         int pad = pw->panner.internal_border;
460
461         if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) {
462             XRectangle *r = pw->panner.shadow_rects;
463
464             r->x = pw->panner.knob_x + pad + pw->panner.knob_width;
465             r->y = pw->panner.knob_y + pad + lw;
466             r->width = pw->panner.shadow_thickness;
467             r->height = pw->panner.knob_height - lw;
468             r++;
469             r->x = pw->panner.knob_x + pad + lw;
470             r->y = pw->panner.knob_y + pad + pw->panner.knob_height;
471             r->width = pw->panner.knob_width - lw + pw->panner.shadow_thickness;
472             r->height = pw->panner.shadow_thickness;
473             pw->panner.shadow_valid = True;
474             return;
475         }
476     }
477     pw->panner.shadow_valid = False;
478 }
479
480 static void
481 scale_knob(PannerWidget pw, Bool location, Bool size)
482 {
483     if (location) {
484         pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x);
485         pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y);
486     }
487     if (size) {
488         Dimension width, height;
489
490         if (pw->panner.slider_width < 1)
491             pw->panner.slider_width = pw->panner.canvas_width;
492         if (pw->panner.slider_height < 1)
493             pw->panner.slider_height = pw->panner.canvas_height;
494         width = Min(pw->panner.slider_width, pw->panner.canvas_width);
495         height = Min(pw->panner.slider_height, pw->panner.canvas_height);
496
497         pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width);
498         pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height);
499     }
500     if (!pw->panner.allow_off)
501         check_knob(pw, True);
502     move_shadow(pw);
503 }
504
505 static void
506 rescale(PannerWidget pw)
507 {
508     int hpad = pw->panner.internal_border << 1;
509     int vpad = hpad;
510
511     if (pw->panner.canvas_width < 1)
512         pw->panner.canvas_width = XtWidth(pw);
513     if (pw->panner.canvas_height < 1)
514         pw->panner.canvas_height = XtHeight(pw);
515
516     if (XtWidth(pw) <= hpad)
517         hpad = 0;
518     if (XtHeight(pw) <= vpad)
519         vpad = 0;
520
521     pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5)
522                          / (double)pw->panner.canvas_width;
523     pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5)
524                          / (double)pw->panner.canvas_height;
525     scale_knob(pw, True, True);
526 }
527
528 static void
529 get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp)
530 {
531     Dimension pad = pw->panner.internal_border << 1;
532
533     *wp = PANNER_DSCALE(pw, pw->panner.canvas_width) + pad;
534     *hp = PANNER_DSCALE(pw, pw->panner.canvas_height) + pad;
535 }
536
537 static Bool
538 get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y)
539 {
540     int pad = pw->panner.internal_border;
541
542     switch (event->type) {
543         case ButtonPress:
544         case ButtonRelease:
545             *x = event->xbutton.x - pad;
546             *y = event->xbutton.y - pad;
547             return (True);
548         case KeyPress:
549         case KeyRelease:
550             *x = event->xkey.x - pad;
551             *y = event->xkey.y - pad;
552             return (True);
553         case EnterNotify:
554         case LeaveNotify:
555             *x = event->xcrossing.x - pad;
556             *y = event->xcrossing.y - pad;
557             return (True);
558         case MotionNotify:
559             *x = event->xmotion.x - pad;
560             *y = event->xmotion.y - pad;
561             return (True);
562     }
563
564     return (False);
565 }
566
567 static int
568 parse_page_string(char *s, int pagesize, int canvassize, Bool *relative)
569 {
570     char *cp;
571     double val = 1.0;
572     Bool rel = False;
573
574     /*
575      * syntax:    spaces [+-] number spaces [pc\0] spaces
576      */
577     for (; isascii(*s) && isspace(*s); s++)     /* skip white space */
578         ;
579
580     if (*s == '+' || *s == '-') {               /* deal with signs */
581         rel = True;
582         if (*s == '-')
583             val = -1.0;
584         s++;
585     }
586     if (!*s) {                          /* if null then return nothing */
587         *relative = True;
588         return (0);
589     }
590
591                                         /* skip over numbers */
592     for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++)
593         ;
594     val *= atof(cp);
595
596                                         /* skip blanks */
597     for (; isascii(*s) && isspace(*s); s++)
598         ;
599
600     if (*s) {                           /* if units */
601         switch (s[0]) {
602             case 'p':
603             case 'P':
604                 val *= (double)pagesize;
605                 break;
606             case 'c':
607             case 'C':
608                 val *= (double)canvassize;
609                 break;
610         }
611     }
612     *relative = rel;
613
614     return ((int)val);
615 }
616
617 #define DRAW_TMP(pw) \
618 { \
619     XDrawRectangle(XtDisplay(pw), XtWindow(pw),                         \
620                    pw->panner.xor_gc,                                   \
621                    pw->panner.tmp.x + pw->panner.internal_border,       \
622                    pw->panner.tmp.y + pw->panner.internal_border,       \
623                    pw->panner.knob_width - 1,                           \
624                    pw->panner.knob_height - 1);                         \
625     pw->panner.tmp.showing = !pw->panner.tmp.showing;                   \
626 }
627
628 #define UNDRAW_TMP(pw) \
629 { \
630     if (pw->panner.tmp.showing)                 \
631       DRAW_TMP(pw);                             \
632 }
633
634 #define BACKGROUND_STIPPLE(pw) \
635 XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name,           \
636                     pw->panner.shadow_color, pw->core.background_pixel, \
637                     pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL)
638
639 #define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap)
640
641 /*ARGSUSED*/
642 static void
643 XawPannerInitialize(Widget greq, Widget gnew, ArgList args, Cardinal *num_args)
644 {
645     PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew;
646     Dimension defwidth, defheight;
647
648     if (req->panner.canvas_width < 1)
649         cnew->panner.canvas_width = 1;
650     if (req->panner.canvas_height < 1)
651         cnew->panner.canvas_height = 1;
652     if (req->panner.default_scale < 1)
653         cnew->panner.default_scale = PANNER_DEFAULT_SCALE;
654
655     get_default_size(req, &defwidth, &defheight);
656     if (XtWidth(req) < 1)
657         XtWidth(cnew) = defwidth;
658     if (XtHeight(req) < 1)
659         XtHeight(cnew) = defheight;
660
661     cnew->panner.shadow_gc = NULL;
662     reset_shadow_gc(cnew);              /* shadowColor */
663     cnew->panner.slider_gc = NULL;
664     reset_slider_gc(cnew);              /* foreground */
665     cnew->panner.xor_gc = NULL;
666     reset_xor_gc(cnew);                 /* foreground ^ background */
667
668     rescale(cnew);                      /* does a position check */
669     cnew->panner.shadow_valid = False;
670     cnew->panner.tmp.doing = False;
671     cnew->panner.tmp.showing = False;
672   }
673
674 static void
675 XawPannerRealize(Widget gw, XtValueMask *valuemaskp,
676                  XSetWindowAttributes *attr)
677 {
678     PannerWidget pw = (PannerWidget)gw;
679     Pixmap pm = XtUnspecifiedPixmap;
680     Bool gotpm = False;
681
682     if (pw->core.background_pixmap == XtUnspecifiedPixmap) {
683         if (pw->panner.stipple_name)
684             pm = BACKGROUND_STIPPLE(pw);
685
686         if (PIXMAP_OKAY(pm)) {
687             attr->background_pixmap = pm;
688             *valuemaskp |= CWBackPixmap;
689             *valuemaskp &= ~CWBackPixel;
690             gotpm = True;
691         }
692     }
693     (*pannerWidgetClass->core_class.superclass->core_class.realize)
694         (gw, valuemaskp, attr);
695
696     if (gotpm)
697         XFreePixmap(XtDisplay(gw), pm);
698 }
699
700 static void
701 XawPannerDestroy(Widget gw)
702 {
703     PannerWidget pw = (PannerWidget)gw;
704
705     XtReleaseGC(gw, pw->panner.shadow_gc);
706     XtReleaseGC(gw, pw->panner.slider_gc);
707     XtReleaseGC(gw, pw->panner.xor_gc);
708 }
709
710 static void
711 XawPannerResize(Widget gw)
712 {
713     rescale((PannerWidget)gw);
714 }
715
716 static void
717 XawPannerRedisplay(Widget gw, XEvent *event, Region region)
718 {
719     PannerWidget pw = (PannerWidget)gw;
720     Display *dpy = XtDisplay(gw);
721     Window w = XtWindow(gw);
722     int pad = pw->panner.internal_border;
723     Dimension lw = pw->panner.line_width;
724     Dimension extra = pw->panner.shadow_thickness + (lw << 1);
725     int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad;
726
727     if (Superclass->core_class.expose)
728         (Superclass->core_class.expose)(gw, event, region);
729
730     pw->panner.tmp.showing = False;
731     XClearArea(XtDisplay(pw), XtWindow(pw),
732                (int)pw->panner.last_x - ((int)lw) + pad,
733                (int)pw->panner.last_y - ((int)lw) + pad,
734                pw->panner.knob_width + extra,
735                pw->panner.knob_height + extra,
736                False);
737     pw->panner.last_x = pw->panner.knob_x;
738     pw->panner.last_y = pw->panner.knob_y;
739
740     XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky,
741                    pw->panner.knob_width - 1, pw->panner.knob_height - 1);
742
743     if (lw)
744         XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky,
745                        pw->panner.knob_width - 1,  pw->panner.knob_height - 1);
746
747     if (pw->panner.shadow_valid)
748         XFillRectangles(dpy, w, pw->panner.shadow_gc, pw->panner.shadow_rects, 2);
749
750     if (pw->panner.tmp.doing && pw->panner.rubber_band)
751         DRAW_TMP(pw);
752 }
753
754 /*ARGSUSED*/
755 static Boolean
756 XawPannerSetValues(Widget gcur, Widget greq, Widget gnew,
757                    ArgList args, Cardinal *num_args)
758 {
759     PannerWidget cur = (PannerWidget)gcur;
760     PannerWidget cnew = (PannerWidget)gnew;
761     Bool redisplay = False;
762
763     if (cur->panner.foreground != cnew->panner.foreground) {
764         reset_slider_gc(cnew);
765         if (cur->panner.foreground != cur->core.background_pixel)
766             reset_xor_gc(cnew);
767         redisplay = True;
768     }
769     else if (cur->panner.line_width != cnew->panner.line_width ||
770              cur->core.background_pixel != cnew->core.background_pixel) {
771         reset_xor_gc(cnew);
772         redisplay = True;
773     }
774     if (cur->panner.shadow_color != cnew->panner.shadow_color) {
775         reset_shadow_gc(cnew);
776         if (cur->panner.foreground == cur->core.background_pixel)
777             reset_xor_gc(cnew);
778         redisplay = True;
779     }
780     if (cur->panner.shadow_thickness != cnew->panner.shadow_thickness) {
781         move_shadow(cnew);
782         redisplay = True;
783     }
784     if (cur->panner.rubber_band != cnew->panner.rubber_band) {
785         reset_xor_gc(cnew);
786         if (cnew->panner.tmp.doing)
787             redisplay = True;
788     }
789
790     if ((cur->panner.stipple_name != cnew->panner.stipple_name
791          || cur->panner.shadow_color != cnew->panner.shadow_color
792          || cur->core.background_pixel != cnew->core.background_pixel)
793         && XtIsRealized(gnew)) {
794         Pixmap pm = cnew->panner.stipple_name ?
795                         BACKGROUND_STIPPLE(cnew) : XtUnspecifiedPixmap;
796
797         if (PIXMAP_OKAY(pm)) {
798             XSetWindowBackgroundPixmap(XtDisplay(cnew), XtWindow(cnew), pm);
799             XFreePixmap(XtDisplay(cnew), pm);
800         }
801         else
802             XSetWindowBackground(XtDisplay(cnew), XtWindow(cnew),
803                                  cnew->core.background_pixel);
804
805         redisplay = True;
806     }
807
808     if (cnew->panner.resize_to_pref &&
809         (cur->panner.canvas_width != cnew->panner.canvas_width
810          || cur->panner.canvas_height != cnew->panner.canvas_height
811          || cur->panner.resize_to_pref != cnew->panner.resize_to_pref)) {
812         get_default_size(cnew, &cnew->core.width, &cnew->core.height);
813         redisplay = True;
814     }
815     else if (cur->panner.canvas_width != cnew->panner.canvas_width
816              || cur->panner.canvas_height != cnew->panner.canvas_height
817              || cur->panner.internal_border != cnew->panner.internal_border) {
818         rescale(cnew);                  /* does a scale_knob as well */
819         redisplay = True;
820     }
821     else {
822         Bool loc = cur->panner.slider_x != cnew->panner.slider_x ||
823                    cur->panner.slider_y != cnew->panner.slider_y;
824         Bool siz = cur->panner.slider_width != cnew->panner.slider_width ||
825                    cur->panner.slider_height != cnew->panner.slider_height;
826         if (loc || siz || (cur->panner.allow_off != cnew->panner.allow_off
827                            && cnew->panner.allow_off)) {
828             scale_knob(cnew, loc, siz);
829             redisplay = True;
830         }
831     }
832
833     return (redisplay);
834 }
835
836 static void
837 XawPannerSetValuesAlmost(Widget gold, Widget gnew, XtWidgetGeometry *req,
838                          XtWidgetGeometry *reply)
839 {
840     if (reply->request_mode == 0)       /* got turned down, so cope */
841         XawPannerResize(gnew);
842
843     (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost)
844         (gold, gnew, req, reply);
845 }
846
847 static XtGeometryResult
848 XawPannerQueryGeometry(Widget gw, XtWidgetGeometry *intended,
849                        XtWidgetGeometry *pref)
850 {
851     PannerWidget pw = (PannerWidget)gw;
852
853     pref->request_mode = (CWWidth | CWHeight);
854     get_default_size(pw, &pref->width, &pref->height);
855
856     if (((intended->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
857         && intended->width == pref->width && intended->height == pref->height)
858         return (XtGeometryYes);
859     else if (pref->width == XtWidth(pw) && pref->height == XtHeight(pw))
860         return (XtGeometryNo);
861
862     return (XtGeometryAlmost);
863 }
864
865
866 /*ARGSUSED*/
867 static void
868 ActionStart(Widget gw, XEvent *event, String *params, Cardinal *num_params)
869 {
870     PannerWidget pw = (PannerWidget)gw;
871     int x, y;
872
873     if (!get_event_xy(pw, event, &x, &y)) {
874         XBell(XtDisplay(gw), 0);
875         return;
876     }
877
878     pw->panner.tmp.doing = True;
879     pw->panner.tmp.startx = pw->panner.knob_x;
880     pw->panner.tmp.starty = pw->panner.knob_y;
881     pw->panner.tmp.dx = x - pw->panner.knob_x;
882     pw->panner.tmp.dy = y - pw->panner.knob_y;
883     pw->panner.tmp.x = pw->panner.knob_x;
884     pw->panner.tmp.y = pw->panner.knob_y;
885     if (pw->panner.rubber_band)
886         DRAW_TMP(pw);
887 }
888
889 /*ARGSUSED*/
890 static void
891 ActionStop(Widget gw, XEvent *event, String *params, Cardinal *num_params)
892 {
893     PannerWidget pw = (PannerWidget)gw;
894     int x, y;
895
896     if (get_event_xy(pw, event, &x, &y)) {
897         pw->panner.tmp.x = x - pw->panner.tmp.dx;
898         pw->panner.tmp.y = y - pw->panner.tmp.dy;
899         if (!pw->panner.allow_off)
900             check_knob(pw, False);
901     }
902     if (pw->panner.rubber_band)
903         DRAW_TMP(pw);
904     pw->panner.tmp.doing = False;
905 }
906
907 static void
908 ActionAbort(Widget gw, XEvent *event, String *params, Cardinal *num_params)
909 {
910     PannerWidget pw = (PannerWidget)gw;
911
912     if (!pw->panner.tmp.doing)
913         return;
914
915     if (pw->panner.rubber_band)
916         UNDRAW_TMP(pw);
917
918     if (!pw->panner.rubber_band) {              /* restore old position */
919         pw->panner.tmp.x = pw->panner.tmp.startx;
920         pw->panner.tmp.y = pw->panner.tmp.starty;
921         ActionNotify(gw, event, params, num_params);
922     }
923     pw->panner.tmp.doing = False;
924 }
925
926 static void
927 ActionMove(Widget gw, XEvent *event, String *params, Cardinal *num_params)
928 {
929     PannerWidget pw = (PannerWidget)gw;
930     int x, y;
931
932     if (!pw->panner.tmp.doing)
933       return;
934
935     if (!get_event_xy(pw, event, &x, &y)) {
936         XBell(XtDisplay(gw), 0);        /* should do error message */
937         return;
938     }
939
940     if (pw->panner.rubber_band)
941         UNDRAW_TMP(pw);
942     pw->panner.tmp.x = x - pw->panner.tmp.dx;
943     pw->panner.tmp.y = y - pw->panner.tmp.dy;
944
945     if (!pw->panner.rubber_band)
946         ActionNotify(gw, event, params, num_params);
947     else {
948         if (!pw->panner.allow_off)
949             check_knob(pw, False);
950         DRAW_TMP(pw);
951     }
952 }
953
954
955 static void
956 ActionPage(Widget gw, XEvent *event, String *params, Cardinal *num_params)
957 {
958     PannerWidget pw = (PannerWidget)gw;
959     Cardinal zero = 0;
960     Bool isin = pw->panner.tmp.doing;
961     int x, y;
962     Bool relx, rely;
963     int pad = pw->panner.internal_border << 1;
964
965     if (*num_params != 2) {
966       XBell(XtDisplay(gw), 0);
967         return;
968     }
969
970     x = parse_page_string(params[0], pw->panner.knob_width,
971                           (int)XtWidth(pw) - pad, &relx);
972     y = parse_page_string(params[1], pw->panner.knob_height,
973                           (int)XtHeight(pw) - pad, &rely);
974
975     if (relx)
976         x += pw->panner.knob_x;
977     if (rely)
978         y += pw->panner.knob_y;
979
980     if (isin) {                         /* if in, then use move */
981         XEvent ev;
982
983         ev.xbutton.type = ButtonPress;
984         ev.xbutton.x = x;
985         ev.xbutton.y = y;
986         ActionMove(gw, &ev, NULL, &zero);
987     }
988     else {
989         pw->panner.tmp.doing = True;
990         pw->panner.tmp.x = x;
991         pw->panner.tmp.y = y;
992         ActionNotify(gw, event, NULL, &zero);
993         pw->panner.tmp.doing = False;
994     }
995 }
996
997 /*ARGSUSED*/
998 static void
999 ActionNotify(Widget gw, XEvent *event, String *params, Cardinal *num_params)
1000 {
1001     PannerWidget pw = (PannerWidget)gw;
1002
1003     if (!pw->panner.tmp.doing)
1004         return;
1005
1006     if (!pw->panner.allow_off)
1007         check_knob(pw, False);
1008     pw->panner.knob_x = pw->panner.tmp.x;
1009     pw->panner.knob_y = pw->panner.tmp.y;
1010     move_shadow(pw);
1011
1012     pw->panner.slider_x = (Position)((double)pw->panner.knob_x
1013                                    / pw->panner.haspect + 0.5);
1014     pw->panner.slider_y = (Position)((double) pw->panner.knob_y
1015                                    / pw->panner.vaspect + 0.5);
1016     if (!pw->panner.allow_off) {
1017         Position tmp;
1018
1019         if (pw->panner.slider_x
1020             > (tmp = (Position)pw->panner.canvas_width -
1021                      (Position)pw->panner.slider_width))
1022             pw->panner.slider_x = tmp;
1023         if (pw->panner.slider_x < 0)
1024             pw->panner.slider_x = 0;
1025         if (pw->panner.slider_y
1026             > (tmp = (Position)pw->panner.canvas_height -
1027                      (Position)pw->panner.slider_height))
1028             pw->panner.slider_y = tmp;
1029         if (pw->panner.slider_y < 0)
1030             pw->panner.slider_y = 0;
1031     }
1032
1033     if (pw->panner.last_x != pw->panner.knob_x ||
1034         pw->panner.last_y != pw->panner.knob_y) {
1035         XawPannerReport rep;
1036
1037         XawPannerRedisplay(gw, NULL, NULL);
1038         rep.changed = XawPRSliderX | XawPRSliderY;
1039         rep.slider_x = pw->panner.slider_x;
1040         rep.slider_y = pw->panner.slider_y;
1041         rep.slider_width = pw->panner.slider_width;
1042         rep.slider_height = pw->panner.slider_height;
1043         rep.canvas_width = pw->panner.canvas_width;
1044         rep.canvas_height = pw->panner.canvas_height;
1045         XtCallCallbackList(gw, pw->panner.report_callbacks, (XtPointer)&rep);
1046     }
1047 }
1048
1049 /*ARGSUSED*/
1050 static void
1051 ActionSet(Widget gw, XEvent *event, String *params, Cardinal *num_params)
1052 {
1053     PannerWidget pw = (PannerWidget)gw;
1054     Bool rb;
1055
1056     if (*num_params < 2 ||
1057         XmuCompareISOLatin1(params[0], "rubberband") != 0) {
1058         XBell(XtDisplay(gw), 0);
1059         return;
1060     }
1061
1062     if (XmuCompareISOLatin1(params[1], "on") == 0)
1063         rb = True;
1064     else if (XmuCompareISOLatin1(params[1], "off") == 0)
1065         rb = False;
1066     else if (XmuCompareISOLatin1(params[1], "toggle") == 0)
1067         rb = !pw->panner.rubber_band;
1068     else {
1069       XBell(XtDisplay(gw), 0);
1070         return;
1071     }
1072
1073     if (rb != pw->panner.rubber_band) {
1074         Arg args[1];
1075
1076         XtSetArg(args[0], XtNrubberBand, rb);
1077         XtSetValues(gw, args, 1);
1078     }
1079 }