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