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(String, 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 #ifndef OLDXAW
327 NULL,
328 #endif
329 },
330 /* panner */
331 {
332 NULL, /* extension */
333 }
334 };
335
336 WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec;
337
338
339 /*
340 * Implementation
341 */
342 static void
reset_shadow_gc(PannerWidget pw)343 reset_shadow_gc(PannerWidget pw)
344 {
345 XtGCMask valuemask = GCForeground;
346 XGCValues values;
347 unsigned long pixels[3];
348
349 if (pw->panner.shadow_gc)
350 XtReleaseGC((Widget)pw, pw->panner.shadow_gc);
351
352 pixels[0] = pw->panner.foreground;
353 pixels[1] = pw->core.background_pixel;
354 pixels[2] = pw->panner.shadow_color;
355
356 if (!pw->panner.stipple_name &&
357 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
358 pixels, 3) &&
359 XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
360 pixels, 2)) {
361 valuemask = GCTile | GCFillStyle;
362 values.fill_style = FillTiled;
363 values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw),
364 pw->panner.foreground,
365 pw->core.background_pixel,
366 pw->core.depth);
367 }
368 else {
369 if (!pw->panner.line_width &&
370 !XmuDistinguishablePixels(XtDisplay(pw), pw->core.colormap,
371 pixels, 2))
372 pw->panner.line_width = 1;
373 valuemask = GCForeground;
374 values.foreground = pw->panner.shadow_color;
375 }
376 if (pw->panner.line_width > 0) {
377 values.line_width = pw->panner.line_width;
378 valuemask |= GCLineWidth;
379 }
380
381 pw->panner.shadow_gc = XtGetGC((Widget)pw, valuemask, &values);
382 }
383
384 static void
reset_slider_gc(PannerWidget pw)385 reset_slider_gc(PannerWidget pw)
386 {
387 XtGCMask valuemask = GCForeground;
388 XGCValues values;
389
390 if (pw->panner.slider_gc)
391 XtReleaseGC((Widget)pw, pw->panner.slider_gc);
392
393 values.foreground = pw->panner.foreground;
394
395 pw->panner.slider_gc = XtGetGC((Widget)pw, valuemask, &values);
396 }
397
398 static void
reset_xor_gc(PannerWidget pw)399 reset_xor_gc(PannerWidget pw)
400 {
401 if (pw->panner.xor_gc)
402 XtReleaseGC((Widget)pw, pw->panner.xor_gc);
403
404 if (pw->panner.rubber_band) {
405 XtGCMask valuemask = (GCForeground | GCFunction);
406 XGCValues values;
407 Pixel tmp;
408
409 tmp = (pw->panner.foreground == pw->core.background_pixel ?
410 pw->panner.shadow_color : pw->panner.foreground);
411 values.foreground = tmp ^ pw->core.background_pixel;
412 values.function = GXxor;
413 if (pw->panner.line_width > 0) {
414 valuemask |= GCLineWidth;
415 values.line_width = pw->panner.line_width;
416 }
417 pw->panner.xor_gc = XtGetGC((Widget)pw, valuemask, &values);
418 }
419 else
420 pw->panner.xor_gc = NULL;
421 }
422
423 static void
check_knob(PannerWidget pw,Bool knob)424 check_knob(PannerWidget pw, Bool knob)
425 {
426 Position pad = (Position)(pw->panner.internal_border << 1);
427 Position maxx = (Position)(XtWidth(pw) - pad - pw->panner.knob_width);
428 Position maxy = (Position)(XtHeight(pw) - pad - pw->panner.knob_height);
429 Position *x = knob ? &pw->panner.knob_x : &pw->panner.tmp.x;
430 Position *y = knob ? &pw->panner.knob_y : &pw->panner.tmp.y;
431
432 /*
433 * note that positions are already normalized (i.e. internal_border
434 * has been subtracted out)
435 */
436 if (*x < 0)
437 *x = 0;
438 if (*x > maxx)
439 *x = maxx;
440
441 if (*y < 0)
442 *y = 0;
443 if (*y > maxy)
444 *y = maxy;
445
446 if (knob) {
447 pw->panner.slider_x = (Position)((double)pw->panner.knob_x
448 / pw->panner.haspect + 0.5);
449 pw->panner.slider_y = (Position)((double)pw->panner.knob_y
450 / pw->panner.vaspect + 0.5);
451 pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE;
452 }
453 }
454
455 static void
move_shadow(PannerWidget pw)456 move_shadow(PannerWidget pw)
457 {
458 if (pw->panner.shadow_thickness > 0) {
459 int lw = pw->panner.shadow_thickness + (pw->panner.line_width << 1);
460 int pad = pw->panner.internal_border;
461
462 if (pw->panner.knob_height > lw && pw->panner.knob_width > lw) {
463 XRectangle *r = pw->panner.shadow_rects;
464
465 r->x = (short)(pw->panner.knob_x + pad + pw->panner.knob_width);
466 r->y = (short)(pw->panner.knob_y + pad + lw);
467 r->width = (unsigned short)(pw->panner.shadow_thickness);
468 r->height = (unsigned short)(pw->panner.knob_height - lw);
469 r++;
470 r->x = (short)(pw->panner.knob_x + pad + lw);
471 r->y = (short)(pw->panner.knob_y + pad + pw->panner.knob_height);
472 r->width = (unsigned short)(pw->panner.knob_width - lw + pw->panner.shadow_thickness);
473 r->height = (unsigned short)(pw->panner.shadow_thickness);
474 pw->panner.shadow_valid = True;
475 return;
476 }
477 }
478 pw->panner.shadow_valid = False;
479 }
480
481 static void
scale_knob(PannerWidget pw,Bool location,Bool size)482 scale_knob(PannerWidget pw, Bool location, Bool size)
483 {
484 if (location) {
485 pw->panner.knob_x = (Position)PANNER_HSCALE(pw, pw->panner.slider_x);
486 pw->panner.knob_y = (Position)PANNER_VSCALE(pw, pw->panner.slider_y);
487 }
488 if (size) {
489 Dimension width, height;
490
491 if (pw->panner.slider_width < 1)
492 pw->panner.slider_width = pw->panner.canvas_width;
493 if (pw->panner.slider_height < 1)
494 pw->panner.slider_height = pw->panner.canvas_height;
495 width = Min(pw->panner.slider_width, pw->panner.canvas_width);
496 height = Min(pw->panner.slider_height, pw->panner.canvas_height);
497
498 pw->panner.knob_width = (Dimension)PANNER_HSCALE(pw, width);
499 pw->panner.knob_height = (Dimension)PANNER_VSCALE(pw, height);
500 }
501 if (!pw->panner.allow_off)
502 check_knob(pw, True);
503 move_shadow(pw);
504 }
505
506 static void
rescale(PannerWidget pw)507 rescale(PannerWidget pw)
508 {
509 int hpad = pw->panner.internal_border << 1;
510 int vpad = hpad;
511
512 if (pw->panner.canvas_width < 1)
513 pw->panner.canvas_width = XtWidth(pw);
514 if (pw->panner.canvas_height < 1)
515 pw->panner.canvas_height = XtHeight(pw);
516
517 if (XtWidth(pw) <= hpad)
518 hpad = 0;
519 if (XtHeight(pw) <= vpad)
520 vpad = 0;
521
522 pw->panner.haspect = ((double)XtWidth(pw) - hpad + .5)
523 / (double)pw->panner.canvas_width;
524 pw->panner.vaspect = ((double)XtHeight(pw) - vpad + .5)
525 / (double)pw->panner.canvas_height;
526 scale_knob(pw, True, True);
527 }
528
529 static void
get_default_size(PannerWidget pw,Dimension * wp,Dimension * hp)530 get_default_size(PannerWidget pw, Dimension *wp, Dimension *hp)
531 {
532 Dimension pad = (Dimension)(pw->panner.internal_border << 1);
533
534 *wp = (Dimension)(PANNER_DSCALE(pw, pw->panner.canvas_width) + pad);
535 *hp = (Dimension)(PANNER_DSCALE(pw, pw->panner.canvas_height) + pad);
536 }
537
538 static Bool
get_event_xy(PannerWidget pw,XEvent * event,int * x,int * y)539 get_event_xy(PannerWidget pw, XEvent *event, int *x, int *y)
540 {
541 int pad = pw->panner.internal_border;
542
543 switch (event->type) {
544 case ButtonPress:
545 case ButtonRelease:
546 *x = event->xbutton.x - pad;
547 *y = event->xbutton.y - pad;
548 return (True);
549 case KeyPress:
550 case KeyRelease:
551 *x = event->xkey.x - pad;
552 *y = event->xkey.y - pad;
553 return (True);
554 case EnterNotify:
555 case LeaveNotify:
556 *x = event->xcrossing.x - pad;
557 *y = event->xcrossing.y - pad;
558 return (True);
559 case MotionNotify:
560 *x = event->xmotion.x - pad;
561 *y = event->xmotion.y - pad;
562 return (True);
563 }
564
565 return (False);
566 }
567
568 static int
parse_page_string(String s,int pagesize,int canvassize,Bool * relative)569 parse_page_string(String s, int pagesize, int canvassize, Bool *relative)
570 {
571 String cp;
572 double val = 1.0;
573 Bool rel = False;
574
575 /*
576 * syntax: spaces [+-] number spaces [pc\0] spaces
577 */
578 for (; isascii(*s) && isspace(*s); s++) /* skip white space */
579 ;
580
581 if (*s == '+' || *s == '-') { /* deal with signs */
582 rel = True;
583 if (*s == '-')
584 val = -1.0;
585 s++;
586 }
587 if (!*s) { /* if null then return nothing */
588 *relative = True;
589 return (0);
590 }
591
592 /* skip over numbers */
593 for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++)
594 ;
595 val *= atof(cp);
596
597 /* skip blanks */
598 for (; isascii(*s) && isspace(*s); s++)
599 ;
600
601 if (*s) { /* if units */
602 switch (s[0]) {
603 case 'p':
604 case 'P':
605 val *= (double)pagesize;
606 break;
607 case 'c':
608 case 'C':
609 val *= (double)canvassize;
610 break;
611 }
612 }
613 *relative = rel;
614
615 return ((int)val);
616 }
617
618 #define DRAW_TMP(pw) \
619 { \
620 XDrawRectangle(XtDisplay(pw), XtWindow(pw), \
621 pw->panner.xor_gc, \
622 (pw->panner.tmp.x + pw->panner.internal_border), \
623 (pw->panner.tmp.y + pw->panner.internal_border), \
624 (unsigned)(pw->panner.knob_width - 1), \
625 (unsigned)(pw->panner.knob_height - 1)); \
626 pw->panner.tmp.showing = !pw->panner.tmp.showing; \
627 }
628
629 #define UNDRAW_TMP(pw) \
630 { \
631 if (pw->panner.tmp.showing) \
632 DRAW_TMP(pw); \
633 }
634
635 #define BACKGROUND_STIPPLE(pw) \
636 XmuLocatePixmapFile(pw->core.screen, pw->panner.stipple_name, \
637 pw->panner.shadow_color, pw->core.background_pixel, \
638 pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL)
639
640 #define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap)
641
642 /*ARGSUSED*/
643 static void
XawPannerInitialize(Widget greq,Widget gnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)644 XawPannerInitialize(Widget greq, Widget gnew, ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
645 {
646 PannerWidget req = (PannerWidget)greq, cnew = (PannerWidget)gnew;
647 Dimension defwidth, defheight;
648
649 if (req->panner.canvas_width < 1)
650 cnew->panner.canvas_width = 1;
651 if (req->panner.canvas_height < 1)
652 cnew->panner.canvas_height = 1;
653 if (req->panner.default_scale < 1)
654 cnew->panner.default_scale = PANNER_DEFAULT_SCALE;
655
656 get_default_size(req, &defwidth, &defheight);
657 if (XtWidth(req) < 1)
658 XtWidth(cnew) = defwidth;
659 if (XtHeight(req) < 1)
660 XtHeight(cnew) = defheight;
661
662 cnew->panner.shadow_gc = NULL;
663 reset_shadow_gc(cnew); /* shadowColor */
664 cnew->panner.slider_gc = NULL;
665 reset_slider_gc(cnew); /* foreground */
666 cnew->panner.xor_gc = NULL;
667 reset_xor_gc(cnew); /* foreground ^ background */
668
669 rescale(cnew); /* does a position check */
670 cnew->panner.shadow_valid = False;
671 cnew->panner.tmp.doing = False;
672 cnew->panner.tmp.showing = False;
673 }
674
675 static void
XawPannerRealize(Widget gw,XtValueMask * valuemaskp,XSetWindowAttributes * attr)676 XawPannerRealize(Widget gw, XtValueMask *valuemaskp,
677 XSetWindowAttributes *attr)
678 {
679 PannerWidget pw = (PannerWidget)gw;
680 Pixmap pm = XtUnspecifiedPixmap;
681 Bool gotpm = False;
682
683 if (pw->core.background_pixmap == XtUnspecifiedPixmap) {
684 if (pw->panner.stipple_name)
685 pm = BACKGROUND_STIPPLE(pw);
686
687 if (PIXMAP_OKAY(pm)) {
688 attr->background_pixmap = pm;
689 *valuemaskp |= CWBackPixmap;
690 *valuemaskp &= (XtValueMask)(~CWBackPixel);
691 gotpm = True;
692 }
693 }
694 (*pannerWidgetClass->core_class.superclass->core_class.realize)
695 (gw, valuemaskp, attr);
696
697 if (gotpm)
698 XFreePixmap(XtDisplay(gw), pm);
699 }
700
701 static void
XawPannerDestroy(Widget gw)702 XawPannerDestroy(Widget gw)
703 {
704 PannerWidget pw = (PannerWidget)gw;
705
706 XtReleaseGC(gw, pw->panner.shadow_gc);
707 XtReleaseGC(gw, pw->panner.slider_gc);
708 XtReleaseGC(gw, pw->panner.xor_gc);
709 }
710
711 static void
XawPannerResize(Widget gw)712 XawPannerResize(Widget gw)
713 {
714 rescale((PannerWidget)gw);
715 }
716
717 static void
XawPannerRedisplay(Widget gw,XEvent * event,Region region)718 XawPannerRedisplay(Widget gw, XEvent *event, Region region)
719 {
720 PannerWidget pw = (PannerWidget)gw;
721 Display *dpy = XtDisplay(gw);
722 Window w = XtWindow(gw);
723 int pad = pw->panner.internal_border;
724 Dimension lw = pw->panner.line_width;
725 Dimension extra = (Dimension)(pw->panner.shadow_thickness + (lw << 1));
726 int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad;
727
728 if (Superclass->core_class.expose)
729 (Superclass->core_class.expose)(gw, event, region);
730
731 pw->panner.tmp.showing = False;
732 XClearArea(XtDisplay(pw), XtWindow(pw),
733 (int)pw->panner.last_x - ((int)lw) + pad,
734 (int)pw->panner.last_y - ((int)lw) + pad,
735 (unsigned)(pw->panner.knob_width + extra),
736 (unsigned)(pw->panner.knob_height + extra),
737 False);
738 pw->panner.last_x = pw->panner.knob_x;
739 pw->panner.last_y = pw->panner.knob_y;
740
741 XFillRectangle(dpy, w, pw->panner.slider_gc, kx, ky,
742 (unsigned)(pw->panner.knob_width - 1),
743 (unsigned)(pw->panner.knob_height - 1));
744
745 if (lw)
746 XDrawRectangle(dpy, w, pw->panner.shadow_gc, kx, ky,
747 (unsigned)(pw->panner.knob_width - 1),
748 (unsigned)(pw->panner.knob_height - 1));
749
750 if (pw->panner.shadow_valid)
751 XFillRectangles(dpy, w, pw->panner.shadow_gc, pw->panner.shadow_rects, 2);
752
753 if (pw->panner.tmp.doing && pw->panner.rubber_band)
754 DRAW_TMP(pw);
755 }
756
757 /*ARGSUSED*/
758 static Boolean
XawPannerSetValues(Widget gcur,Widget greq _X_UNUSED,Widget gnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)759 XawPannerSetValues(Widget gcur, Widget greq _X_UNUSED, Widget gnew,
760 ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
761 {
762 PannerWidget cur = (PannerWidget)gcur;
763 PannerWidget cnew = (PannerWidget)gnew;
764 Bool redisplay = False;
765
766 if (cur->panner.foreground != cnew->panner.foreground) {
767 reset_slider_gc(cnew);
768 if (cur->panner.foreground != cur->core.background_pixel)
769 reset_xor_gc(cnew);
770 redisplay = True;
771 }
772 else if (cur->panner.line_width != cnew->panner.line_width ||
773 cur->core.background_pixel != cnew->core.background_pixel) {
774 reset_xor_gc(cnew);
775 redisplay = True;
776 }
777 if (cur->panner.shadow_color != cnew->panner.shadow_color) {
778 reset_shadow_gc(cnew);
779 if (cur->panner.foreground == cur->core.background_pixel)
780 reset_xor_gc(cnew);
781 redisplay = True;
782 }
783 if (cur->panner.shadow_thickness != cnew->panner.shadow_thickness) {
784 move_shadow(cnew);
785 redisplay = True;
786 }
787 if (cur->panner.rubber_band != cnew->panner.rubber_band) {
788 reset_xor_gc(cnew);
789 if (cnew->panner.tmp.doing)
790 redisplay = True;
791 }
792
793 if ((cur->panner.stipple_name != cnew->panner.stipple_name
794 || cur->panner.shadow_color != cnew->panner.shadow_color
795 || cur->core.background_pixel != cnew->core.background_pixel)
796 && XtIsRealized(gnew)) {
797 Pixmap pm = cnew->panner.stipple_name ?
798 BACKGROUND_STIPPLE(cnew) : XtUnspecifiedPixmap;
799
800 if (PIXMAP_OKAY(pm)) {
801 XSetWindowBackgroundPixmap(XtDisplay(cnew), XtWindow(cnew), pm);
802 XFreePixmap(XtDisplay(cnew), pm);
803 }
804 else
805 XSetWindowBackground(XtDisplay(cnew), XtWindow(cnew),
806 cnew->core.background_pixel);
807
808 redisplay = True;
809 }
810
811 if (cnew->panner.resize_to_pref &&
812 (cur->panner.canvas_width != cnew->panner.canvas_width
813 || cur->panner.canvas_height != cnew->panner.canvas_height
814 || cur->panner.resize_to_pref != cnew->panner.resize_to_pref)) {
815 get_default_size(cnew, &cnew->core.width, &cnew->core.height);
816 redisplay = True;
817 }
818 else if (cur->panner.canvas_width != cnew->panner.canvas_width
819 || cur->panner.canvas_height != cnew->panner.canvas_height
820 || cur->panner.internal_border != cnew->panner.internal_border) {
821 rescale(cnew); /* does a scale_knob as well */
822 redisplay = True;
823 }
824 else {
825 Bool loc = cur->panner.slider_x != cnew->panner.slider_x ||
826 cur->panner.slider_y != cnew->panner.slider_y;
827 Bool siz = cur->panner.slider_width != cnew->panner.slider_width ||
828 cur->panner.slider_height != cnew->panner.slider_height;
829 if (loc || siz || (cur->panner.allow_off != cnew->panner.allow_off
830 && cnew->panner.allow_off)) {
831 scale_knob(cnew, loc, siz);
832 redisplay = True;
833 }
834 }
835
836 return (Boolean)(redisplay);
837 }
838
839 static void
XawPannerSetValuesAlmost(Widget gold,Widget gnew,XtWidgetGeometry * req,XtWidgetGeometry * reply)840 XawPannerSetValuesAlmost(Widget gold, Widget gnew, XtWidgetGeometry *req,
841 XtWidgetGeometry *reply)
842 {
843 if (reply->request_mode == 0) /* got turned down, so cope */
844 XawPannerResize(gnew);
845
846 (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost)
847 (gold, gnew, req, reply);
848 }
849
850 static XtGeometryResult
XawPannerQueryGeometry(Widget gw,XtWidgetGeometry * intended,XtWidgetGeometry * pref)851 XawPannerQueryGeometry(Widget gw, XtWidgetGeometry *intended,
852 XtWidgetGeometry *pref)
853 {
854 PannerWidget pw = (PannerWidget)gw;
855
856 pref->request_mode = (CWWidth | CWHeight);
857 get_default_size(pw, &pref->width, &pref->height);
858
859 if (((intended->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight))
860 && intended->width == pref->width && intended->height == pref->height)
861 return (XtGeometryYes);
862 else if (pref->width == XtWidth(pw) && pref->height == XtHeight(pw))
863 return (XtGeometryNo);
864
865 return (XtGeometryAlmost);
866 }
867
868
869 /*ARGSUSED*/
870 static void
ActionStart(Widget gw,XEvent * event,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)871 ActionStart(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
872 {
873 PannerWidget pw = (PannerWidget)gw;
874 int x, y;
875
876 if (!get_event_xy(pw, event, &x, &y)) {
877 XBell(XtDisplay(gw), 0);
878 return;
879 }
880
881 pw->panner.tmp.doing = True;
882 pw->panner.tmp.startx = pw->panner.knob_x;
883 pw->panner.tmp.starty = pw->panner.knob_y;
884 pw->panner.tmp.dx = (Position)(x - pw->panner.knob_x);
885 pw->panner.tmp.dy = (Position)(y - pw->panner.knob_y);
886 pw->panner.tmp.x = pw->panner.knob_x;
887 pw->panner.tmp.y = pw->panner.knob_y;
888 if (pw->panner.rubber_band)
889 DRAW_TMP(pw);
890 }
891
892 /*ARGSUSED*/
893 static void
ActionStop(Widget gw,XEvent * event,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)894 ActionStop(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
895 {
896 PannerWidget pw = (PannerWidget)gw;
897 int x, y;
898
899 if (get_event_xy(pw, event, &x, &y)) {
900 pw->panner.tmp.x = (Position)(x - pw->panner.tmp.dx);
901 pw->panner.tmp.y = (Position)(y - pw->panner.tmp.dy);
902 if (!pw->panner.allow_off)
903 check_knob(pw, False);
904 }
905 if (pw->panner.rubber_band)
906 DRAW_TMP(pw);
907 pw->panner.tmp.doing = False;
908 }
909
910 static void
ActionAbort(Widget gw,XEvent * event,String * params,Cardinal * num_params)911 ActionAbort(Widget gw, XEvent *event, String *params, Cardinal *num_params)
912 {
913 PannerWidget pw = (PannerWidget)gw;
914
915 if (!pw->panner.tmp.doing)
916 return;
917
918 if (pw->panner.rubber_band)
919 UNDRAW_TMP(pw);
920
921 if (!pw->panner.rubber_band) { /* restore old position */
922 pw->panner.tmp.x = pw->panner.tmp.startx;
923 pw->panner.tmp.y = pw->panner.tmp.starty;
924 ActionNotify(gw, event, params, num_params);
925 }
926 pw->panner.tmp.doing = False;
927 }
928
929 static void
ActionMove(Widget gw,XEvent * event,String * params,Cardinal * num_params)930 ActionMove(Widget gw, XEvent *event, String *params, Cardinal *num_params)
931 {
932 PannerWidget pw = (PannerWidget)gw;
933 int x, y;
934
935 if (!pw->panner.tmp.doing)
936 return;
937
938 if (!get_event_xy(pw, event, &x, &y)) {
939 XBell(XtDisplay(gw), 0); /* should do error message */
940 return;
941 }
942
943 if (pw->panner.rubber_band)
944 UNDRAW_TMP(pw);
945 pw->panner.tmp.x = (Position)(x - pw->panner.tmp.dx);
946 pw->panner.tmp.y = (Position)(y - pw->panner.tmp.dy);
947
948 if (!pw->panner.rubber_band)
949 ActionNotify(gw, event, params, num_params);
950 else {
951 if (!pw->panner.allow_off)
952 check_knob(pw, False);
953 DRAW_TMP(pw);
954 }
955 }
956
957
958 static void
ActionPage(Widget gw,XEvent * event,String * params,Cardinal * num_params)959 ActionPage(Widget gw, XEvent *event, String *params, Cardinal *num_params)
960 {
961 PannerWidget pw = (PannerWidget)gw;
962 Cardinal zero = 0;
963 Bool isin = pw->panner.tmp.doing;
964 int x, y;
965 Bool relx, rely;
966 int pad = pw->panner.internal_border << 1;
967
968 if (*num_params != 2) {
969 XBell(XtDisplay(gw), 0);
970 return;
971 }
972
973 x = parse_page_string(params[0], pw->panner.knob_width,
974 (int)XtWidth(pw) - pad, &relx);
975 y = parse_page_string(params[1], pw->panner.knob_height,
976 (int)XtHeight(pw) - pad, &rely);
977
978 if (relx)
979 x += pw->panner.knob_x;
980 if (rely)
981 y += pw->panner.knob_y;
982
983 if (isin) { /* if in, then use move */
984 XEvent ev;
985
986 ev.xbutton.type = ButtonPress;
987 ev.xbutton.x = x;
988 ev.xbutton.y = y;
989 ActionMove(gw, &ev, NULL, &zero);
990 }
991 else {
992 pw->panner.tmp.doing = True;
993 pw->panner.tmp.x = (Position)x;
994 pw->panner.tmp.y = (Position)y;
995 ActionNotify(gw, event, NULL, &zero);
996 pw->panner.tmp.doing = False;
997 }
998 }
999
1000 /*ARGSUSED*/
1001 static void
ActionNotify(Widget gw,XEvent * event _X_UNUSED,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)1002 ActionNotify(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
1003 {
1004 PannerWidget pw = (PannerWidget)gw;
1005
1006 if (!pw->panner.tmp.doing)
1007 return;
1008
1009 if (!pw->panner.allow_off)
1010 check_knob(pw, False);
1011 pw->panner.knob_x = pw->panner.tmp.x;
1012 pw->panner.knob_y = pw->panner.tmp.y;
1013 move_shadow(pw);
1014
1015 pw->panner.slider_x = (Position)((double)pw->panner.knob_x
1016 / pw->panner.haspect + 0.5);
1017 pw->panner.slider_y = (Position)((double) pw->panner.knob_y
1018 / pw->panner.vaspect + 0.5);
1019 if (!pw->panner.allow_off) {
1020 Position tmp;
1021
1022 if (pw->panner.slider_x
1023 > (tmp = (Position)(pw->panner.canvas_width -
1024 pw->panner.slider_width)))
1025 pw->panner.slider_x = tmp;
1026 if (pw->panner.slider_x < 0)
1027 pw->panner.slider_x = 0;
1028 if (pw->panner.slider_y
1029 > (tmp = (Position)(pw->panner.canvas_height -
1030 pw->panner.slider_height)))
1031 pw->panner.slider_y = tmp;
1032 if (pw->panner.slider_y < 0)
1033 pw->panner.slider_y = 0;
1034 }
1035
1036 if (pw->panner.last_x != pw->panner.knob_x ||
1037 pw->panner.last_y != pw->panner.knob_y) {
1038 XawPannerReport rep;
1039
1040 XawPannerRedisplay(gw, NULL, NULL);
1041 rep.changed = XawPRSliderX | XawPRSliderY;
1042 rep.slider_x = pw->panner.slider_x;
1043 rep.slider_y = pw->panner.slider_y;
1044 rep.slider_width = pw->panner.slider_width;
1045 rep.slider_height = pw->panner.slider_height;
1046 rep.canvas_width = pw->panner.canvas_width;
1047 rep.canvas_height = pw->panner.canvas_height;
1048 XtCallCallbackList(gw, pw->panner.report_callbacks, (XtPointer)&rep);
1049 }
1050 }
1051
1052 /*ARGSUSED*/
1053 static void
ActionSet(Widget gw,XEvent * event _X_UNUSED,String * params,Cardinal * num_params)1054 ActionSet(Widget gw, XEvent *event _X_UNUSED, String *params, Cardinal *num_params)
1055 {
1056 PannerWidget pw = (PannerWidget)gw;
1057 Bool rb;
1058
1059 if (*num_params < 2 ||
1060 XmuCompareISOLatin1(params[0], "rubberband") != 0) {
1061 XBell(XtDisplay(gw), 0);
1062 return;
1063 }
1064
1065 if (XmuCompareISOLatin1(params[1], "on") == 0)
1066 rb = True;
1067 else if (XmuCompareISOLatin1(params[1], "off") == 0)
1068 rb = False;
1069 else if (XmuCompareISOLatin1(params[1], "toggle") == 0)
1070 rb = !pw->panner.rubber_band;
1071 else {
1072 XBell(XtDisplay(gw), 0);
1073 return;
1074 }
1075
1076 if (rb != pw->panner.rubber_band) {
1077 Arg args[1];
1078
1079 XtSetArg(args[0], XtNrubberBand, rb);
1080 XtSetValues(gw, args, 1);
1081 }
1082 }
1083