1 /*
2  * $XConsortium: Panner.c,v 1.52 95/01/10 14:31:26 kaleb Exp $
3  *
4  Copyright (c) 1989, 1994  X Consortium
5 
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in
14  all copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19  X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 
23  Except as contained in this notice, the name of the X Consortium shall not be
24  used in advertising or otherwise to promote the sale, use or other dealings
25  in this Software without prior written authorization from the X Consortium.
26  *
27  * Author:  Jim Fulton, MIT X Consortium
28  */
29 
30 #include "xdvi-config.h"
31 #include "xdvi.h"
32 #include "string-utils.h"
33 
34 #if defined(MOTIF) && defined(USE_PANNER) && USE_XAW_PANNER /* entire file */
35 
36 #if defined(__GNUC__) && DEVEL_MODE
37 #warning COMPILING Panner_c
38 #endif
39 
40 #include <X11/IntrinsicP.h>
41 #include <X11/StringDefs.h>		/* for XtN and XtC defines */
42 #include <X11/Xmu/CharSet.h>		/* for XmuCompareISOLatin1() */
43 #include "PannerP.h"		/* us */
44 #include <X11/Xos.h>
45 #include <X11/Xmu/Misc.h>		/* for Min */
46 #include <X11/Xmu/Drawing.h>
47 #include <ctype.h>			/* for isascii() etc. */
48 
49 extern Bool XmuDistinguishablePixels(); /* not defined in any Xmu headers */
50 
51 #if HAVE_XKB_BELL_EXT
52 # include <X11/XKBlib.h>
53 # define panBell(display, window, percent)	\
54 	 XkbBell(display, window, percent, (Atom) None)
55 #else
56 # define panBell(display, window, percent)	XBell(display, percent)
57 #endif
58 
59 /*
60   ======================================================================
61   begin copy from Simple.c
62   ======================================================================
63 */
64 
65 #define offset(field) XtOffsetOf(SimpleRec, simple.field)
66 
67 static XtResource resources[] = {
68     {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
69      offset(cursor), XtRImmediate, (XtPointer) None},
70     {XtNinsensitiveBorder, XtCInsensitive, XtRPixmap, sizeof(Pixmap),
71      offset(insensitive_border), XtRImmediate, (XtPointer) NULL},
72     {XtNpointerColor, XtCForeground, XtRPixel, sizeof(Pixel),
73      offset(pointer_fg), XtRString, XtDefaultForeground},
74     {XtNpointerColorBackground, XtCBackground, XtRPixel, sizeof(Pixel),
75      offset(pointer_bg), XtRString, XtDefaultBackground},
76     {XtNcursorName, XtCCursor, XtRString, sizeof(String),
77      offset(cursor_name), XtRString, NULL},
78     {XtNinternational, XtCInternational, XtRBoolean, sizeof(Boolean),
79      offset(international), XtRImmediate, (XtPointer) FALSE},
80 #undef offset
81 };
82 
83 static void ClassPartInitialize(), ClassInitialize(),Realize(),ConvertCursor();
84 static Bool SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args);
85 static Bool ChangeSensitive(Widget w);
86 
87 SimpleClassRec simpleClassRec = {
88     { /* core fields */
89 	/* superclass		*/	(WidgetClass) &widgetClassRec,
90 	/* class_name		*/	"Simple",
91 	/* widget_size		*/	sizeof(SimpleRec),
92 	/* class_initialize		*/	ClassInitialize,
93 	/* class_part_initialize	*/	ClassPartInitialize,
94 	/* class_inited		*/	FALSE,
95 	/* initialize		*/	NULL,
96 	/* initialize_hook		*/	NULL,
97 	/* realize			*/	Realize,
98 	/* actions			*/	NULL,
99 	/* num_actions		*/	0,
100 	/* resources		*/	resources,
101 	/* num_resources		*/	XtNumber(resources),
102 	/* xrm_class		*/	NULLQUARK,
103 	/* compress_motion		*/	TRUE,
104 	/* compress_exposure	*/	TRUE,
105 	/* compress_enterleave	*/	TRUE,
106 	/* visible_interest		*/	FALSE,
107 	/* destroy			*/	NULL,
108 	/* resize			*/	NULL,
109 	/* expose			*/	NULL,
110 #warning FIXME: incompatible pointer type
111 	/* set_values		*/	SetValues,
112 	/* set_values_hook		*/	NULL,
113 	/* set_values_almost	*/	XtInheritSetValuesAlmost,
114 	/* get_values_hook		*/	NULL,
115 	/* accept_focus		*/	NULL,
116 	/* version			*/	XtVersion,
117 	/* callback_private		*/	NULL,
118 	/* tm_table			*/	NULL,
119 	/* query_geometry		*/	XtInheritQueryGeometry,
120 	/* display_accelerator	*/	XtInheritDisplayAccelerator,
121 	/* extension		*/	NULL
122     },
123     { /* simple fields */
124 	/* change_sensitive		*/	ChangeSensitive
125 #ifndef HAVE_OLD_XAW
126 	, NULL
127 #endif
128     }
129 };
130 
131 WidgetClass simpleWidgetClass = (WidgetClass)&simpleClassRec;
132 
ClassInitialize()133 static void ClassInitialize()
134 {
135     static XtConvertArgRec convertArg[] = {
136         {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.screen),
137 	 sizeof(Screen *)},
138         {XtResourceString, (XtPointer) XtNpointerColor, sizeof(Pixel)},
139         {XtResourceString, (XtPointer) XtNpointerColorBackground,
140 	 sizeof(Pixel)},
141         {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.colormap),
142 	 sizeof(Colormap)}
143     };
144 
145     XawInitializeWidgetSet();
146     XtSetTypeConverter( XtRString, XtRColorCursor, XmuCvtStringToColorCursor,
147 			convertArg, XtNumber(convertArg),
148 			XtCacheByDisplay, (XtDestructor)NULL);
149 }
150 
ClassPartInitialize(class)151 static void ClassPartInitialize(class)
152     WidgetClass class;
153 {
154     SimpleWidgetClass c     = (SimpleWidgetClass) class;
155     SimpleWidgetClass super = (SimpleWidgetClass)
156 	c->core_class.superclass;
157 
158     if (c->simple_class.change_sensitive == NULL) {
159 	char buf[BUFSIZ];
160 
161 	(void) sprintf(buf,
162 		       "%s Widget: The Simple Widget class method 'change_sensitive' is undefined.\nA function must be defined or inherited.",
163 		       c->core_class.class_name);
164 	XtWarning(buf);
165 	c->simple_class.change_sensitive = ChangeSensitive;
166     }
167 
168     if (c->simple_class.change_sensitive == XtInheritChangeSensitive)
169 	c->simple_class.change_sensitive = super->simple_class.change_sensitive;
170 }
171 
Realize(w,valueMask,attributes)172 static void Realize(w, valueMask, attributes)
173     Widget w;
174     Mask *valueMask;
175     XSetWindowAttributes *attributes;
176 {
177     Pixmap border_pixmap = 0;
178     if (!XtIsSensitive(w)) {
179 	/* change border to gray; have to remember the old one,
180 	 * so XtDestroyWidget deletes the proper one */
181 	if (((SimpleWidget)w)->simple.insensitive_border == None)
182 	    ((SimpleWidget)w)->simple.insensitive_border =
183 		XmuCreateStippledPixmap(XtScreen(w),
184 					w->core.border_pixel,
185 					w->core.background_pixel,
186 					w->core.depth);
187         border_pixmap = w->core.border_pixmap;
188 	attributes->border_pixmap =
189 	    w->core.border_pixmap = ((SimpleWidget)w)->simple.insensitive_border;
190 
191 	*valueMask |= CWBorderPixmap;
192 	*valueMask &= ~CWBorderPixel;
193     }
194 
195     ConvertCursor(w);
196 
197     if ((attributes->cursor = ((SimpleWidget)w)->simple.cursor) != None)
198 	*valueMask |= CWCursor;
199 
200     XtCreateWindow( w, (unsigned int)InputOutput, (Visual *)CopyFromParent,
201 		    *valueMask, attributes );
202 
203     if (!XtIsSensitive(w))
204 	w->core.border_pixmap = border_pixmap;
205 }
206 
207 /*	Function Name: ConvertCursor
208  *	Description: Converts a name to a new cursor.
209  *	Arguments: w - the simple widget.
210  *	Returns: none.
211  */
212 
213 static void
ConvertCursor(w)214 ConvertCursor(w)
215     Widget w;
216 {
217     SimpleWidget simple = (SimpleWidget) w;
218     XrmValue from, to;
219     Cursor cursor;
220 
221     if (simple->simple.cursor_name == NULL)
222 	return;
223 
224     from.addr = (XPointer) simple->simple.cursor_name;
225     from.size = strlen((char *) from.addr) + 1;
226 
227     to.size = sizeof(Cursor);
228     to.addr = (XPointer) &cursor;
229 
230     if (XtConvertAndStore(w, XtRString, &from, XtRColorCursor, &to)) {
231 	if ( cursor !=  None)
232 	    simple->simple.cursor = cursor;
233     }
234     else {
235 	XtAppErrorMsg(XtWidgetToApplicationContext(w),
236 		      "convertFailed","ConvertCursor","XawError",
237 		      "Simple: ConvertCursor failed.",
238 		      (String *)NULL, (Cardinal *)NULL);
239     }
240 }
241 
242 
243 /* ARGSUSED */
244 static Bool
SetValues(Widget current,Widget request,Widget new,ArgList args,Cardinal * num_args)245 SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args)
246 {
247     SimpleWidget s_old = (SimpleWidget) current;
248     SimpleWidget s_new = (SimpleWidget) new;
249     Boolean new_cursor = FALSE;
250 
251     UNUSED(request);
252     UNUSED(args);
253     UNUSED(num_args);
254     /* this disables user changes after creation*/
255     s_new->simple.international = s_old->simple.international;
256 
257     if ( XtIsSensitive(current) != XtIsSensitive(new) )
258 	(*((SimpleWidgetClass)XtClass(new))->
259 	 simple_class.change_sensitive) ( new );
260 
261     if (s_old->simple.cursor != s_new->simple.cursor) {
262 	new_cursor = TRUE;
263     }
264 
265     /*
266      * We are not handling the string cursor_name correctly here.
267      */
268 
269     if ( (s_old->simple.pointer_fg != s_new->simple.pointer_fg) ||
270 	 (s_old->simple.pointer_bg != s_new->simple.pointer_bg) ||
271 	 (s_old->simple.cursor_name != s_new->simple.cursor_name) ) {
272 	ConvertCursor(new);
273 	new_cursor = TRUE;
274     }
275 
276     if (new_cursor && XtIsRealized(new))
277         XDefineCursor(XtDisplay(new), XtWindow(new), s_new->simple.cursor);
278 
279     return False;
280 }
281 
282 
ChangeSensitive(Widget w)283 static Bool ChangeSensitive(Widget w)
284 {
285     if (XtIsRealized(w)) {
286 	if (XtIsSensitive(w))
287 	    if (w->core.border_pixmap != XtUnspecifiedPixmap)
288 		XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w),
289 				        w->core.border_pixmap );
290 	    else
291 		XSetWindowBorder( XtDisplay(w), XtWindow(w),
292 				  w->core.border_pixel );
293 	else {
294 	    if (((SimpleWidget)w)->simple.insensitive_border == None)
295 		((SimpleWidget)w)->simple.insensitive_border =
296 		    XmuCreateStippledPixmap(XtScreen(w),
297 					    w->core.border_pixel,
298 					    w->core.background_pixel,
299 					    w->core.depth);
300 	    XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w),
301 				    ((SimpleWidget)w)->
302 				    simple.insensitive_border );
303 	}
304     }
305     return False;
306 }
307 
308 /*
309   ======================================================================
310   end copy from Simple.c
311   ======================================================================
312 */
313 
314 
315 /* following function copied from XawInit.c */
XawInitializeWidgetSet()316 void XawInitializeWidgetSet ()
317 {
318     static int firsttime = 1;
319 
320     if (firsttime) {
321 	firsttime = 0;
322 	XtInitializeWidgetClass (vendorShellWidgetClass);
323     }
324 }
325 
326 static char defaultTranslations[] =
327 "<Btn1Down>:    start() \n\
328    <Btn1Motion>:  move() \n\
329    <Btn1Up>:      notify() stop() \n\
330    <Btn2Down>:    abort() \n\
331    :<Key>KP_Enter: set(rubberband,toggle) \n\
332    <Key>space:    page(+1p,+1p) \n\
333    <Key>Delete:   page(-1p,-1p) \n\
334    :<Key>KP_Delete: page(-1p,-1p) \n\
335    <Key>BackSpace: page(-1p,-1p) \n\
336    <Key>Left:     page(-.5p,+0) \n\
337    :<Key>KP_Left:  page(-.5p,+0) \n\
338    <Key>Right:    page(+.5p,+0) \n\
339    :<Key>KP_Right: page(+.5p,+0) \n\
340    <Key>Up:       page(+0,-.5p) \n\
341    :<Key>KP_Up:    page(+0,-.5p) \n\
342    <Key>Down:     page(+0,+.5p) \n\
343    :<Key>KP_Down:  page(+0,+.5p) \n\
344    <Key>Home:     page(0,0) \n\
345    :<Key>KP_Home:  page(0,0)";
346 
347 
348 static void ActionStart(), ActionStop(), ActionAbort(), ActionMove();
349 static void ActionPage(), ActionNotify(), ActionSet();
350 
351 static XtActionsRec actions[] = {
352     { "start", ActionStart },		/* start tmp graphics */
353     { "stop", ActionStop },		/* stop tmp graphics */
354     { "abort", ActionAbort },		/* punt */
355     { "move", ActionMove },		/* move tmp graphics on Motion event */
356     { "page", ActionPage },		/* page around usually from keyboard */
357     { "notify", ActionNotify },		/* callback new position */
358     { "set", ActionSet },		/* set various parameters */
359 };
360 
361 
362 /*
363  * resources for the panner
364  */
365 static XtResource panner_resources[] = {
366 #define poff(field) XtOffsetOf(PannerRec, panner.field)
367     { XtNallowOff, XtCAllowOff, XtRBoolean, sizeof(Boolean),
368       poff(allow_off), XtRImmediate, (XtPointer) FALSE },
369     { XtNresize, XtCResize, XtRBoolean, sizeof(Boolean),
370       poff(resize_to_pref), XtRImmediate, (XtPointer) TRUE },
371     { XtNreportCallback, XtCReportCallback, XtRCallback, sizeof(XtPointer),
372       poff(report_callbacks), XtRCallback, (XtPointer) NULL },
373     { XtNdefaultScale, XtCDefaultScale, XtRDimension, sizeof(Dimension),
374       poff(default_scale), XtRImmediate, (XtPointer) PANNER_DEFAULT_SCALE },
375     { XtNrubberBand, XtCRubberBand, XtRBoolean, sizeof(Boolean),
376       poff(rubber_band), XtRImmediate, (XtPointer) FALSE },
377     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
378       poff(foreground), XtRString, (XtPointer) XtDefaultBackground },
379     { XtNinternalSpace, XtCInternalSpace, XtRDimension, sizeof(Dimension),
380       poff(internal_border), XtRImmediate, (XtPointer) 4 },
381     { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof(Dimension),
382       poff(line_width), XtRImmediate, (XtPointer) 0 },
383     { XtNcanvasWidth, XtCCanvasWidth, XtRDimension, sizeof(Dimension),
384       poff(canvas_width), XtRImmediate, (XtPointer) 0 },
385     { XtNcanvasHeight, XtCCanvasHeight, XtRDimension, sizeof(Dimension),
386       poff(canvas_height), XtRImmediate, (XtPointer) 0 },
387     { XtNsliderX, XtCSliderX, XtRPosition, sizeof(Position),
388       poff(slider_x), XtRImmediate, (XtPointer) 0 },
389     { XtNsliderY, XtCSliderY, XtRPosition, sizeof(Position),
390       poff(slider_y), XtRImmediate, (XtPointer) 0 },
391     { XtNsliderWidth, XtCSliderWidth, XtRDimension, sizeof(Dimension),
392       poff(slider_width), XtRImmediate, (XtPointer) 0 },
393     { XtNsliderHeight, XtCSliderHeight, XtRDimension, sizeof(Dimension),
394       poff(slider_height), XtRImmediate, (XtPointer) 0 },
395     { XtNshadowColor, XtCShadowColor, XtRPixel, sizeof(Pixel),
396       poff(shadow_color), XtRString, (XtPointer) XtDefaultForeground },
397     { XtNshadowThickness, XtCShadowThickness, XtRDimension, sizeof(Dimension),
398       poff(shadow_thickness), XtRImmediate, (XtPointer) 2 },
399     { XtNbackgroundStipple, XtCBackgroundStipple, XtRString, sizeof(String),
400       poff(stipple_name), XtRImmediate, (XtPointer) NULL },
401 #undef poff
402 };
403 
404 
405 /*
406  * widget class methods used below
407  */
408 static void Initialize();		/* create gc's */
409 static void PannerRealize();			/* create window */
410 static void Destroy();			/* clean up widget */
411 static void Resize();			/* need to rescale ourselves */
412 static void Redisplay();		/* draw ourselves */
413 static Boolean PannerSetValues();		/* set all of the resources */
414 static void SetValuesAlmost();		/* deal with failed setval geom req */
415 static XtGeometryResult QueryGeometry();  /* say how big we would like to be */
416 
417 PannerClassRec pannerClassRec = {
418     { /* core fields */
419 	/* superclass		*/	(WidgetClass) &simpleClassRec,
420 	/* class_name		*/	"Panner",
421 	/* widget_size		*/	sizeof(PannerRec),
422 	/* class_initialize		*/	XawInitializeWidgetSet,
423 	/* class_part_initialize	*/	NULL,
424 	/* class_inited		*/	FALSE,
425 	/* initialize		*/	Initialize,
426 	/* initialize_hook		*/	NULL,
427 	/* realize			*/	PannerRealize,
428 	/* actions			*/	actions,
429 	/* num_actions		*/	XtNumber(actions),
430 	/* resources		*/	panner_resources,
431 	/* num_resources		*/	XtNumber(panner_resources),
432 	/* xrm_class		*/	NULLQUARK,
433 	/* compress_motion		*/	TRUE,
434 	/* compress_exposure	*/	TRUE,
435 	/* compress_enterleave	*/	TRUE,
436 	/* visible_interest		*/	FALSE,
437 	/* destroy			*/	Destroy,
438 	/* resize			*/	Resize,
439 	/* expose			*/	Redisplay,
440 	/* set_values		*/	PannerSetValues,
441 	/* set_values_hook		*/	NULL,
442 	/* set_values_almost	*/	SetValuesAlmost,
443 	/* get_values_hook		*/	NULL,
444 	/* accept_focus		*/	NULL,
445 	/* version			*/	XtVersion,
446 	/* callback_private		*/	NULL,
447 	/* tm_table			*/	defaultTranslations,
448 	/* query_geometry		*/	QueryGeometry,
449 	/* display_accelerator	*/	XtInheritDisplayAccelerator,
450 	/* extension		*/	NULL
451     },
452     { /* simple fields */
453 	/* change_sensitive		*/	XtInheritChangeSensitive
454 #ifndef HAVE_OLD_XAW
455 	, NULL
456 #endif
457     },
458     { /* panner fields */
459 	/* ignore                   */	0
460     }
461 };
462 
463 WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec;
464 
465 
466 /*****************************************************************************
467  *                                                                           *
468  *			    panner utility routines                          *
469  *                                                                           *
470  *****************************************************************************/
471 
reset_shadow_gc(pw)472 static void reset_shadow_gc (pw)	/* used when resources change */
473     PannerWidget pw;
474 {
475     XtGCMask valuemask = GCForeground;
476     XGCValues values;
477     unsigned long   pixels[3];
478 
479     if (pw->panner.shadow_gc) XtReleaseGC ((Widget) pw, pw->panner.shadow_gc);
480 
481     pixels[0] = pw->panner.foreground;
482     pixels[1] = pw->core.background_pixel;
483     pixels[2] = pw->panner.shadow_color;
484     if (!pw->panner.stipple_name &&
485 	!XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
486 				   pixels, 3) &&
487 	XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
488 				  pixels, 2))
489     {
490 	valuemask = GCTile | GCFillStyle;
491 	values.fill_style = FillTiled;
492 	values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw),
493 					      pw->panner.foreground,
494 					      pw->core.background_pixel,
495 					      pw->core.depth);
496     }
497     else
498     {
499 	if (!pw->panner.line_width &&
500 	    !XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap,
501 				       pixels, 2))
502 	    pw->panner.line_width = 1;
503 	valuemask = GCForeground;
504 	values.foreground = pw->panner.shadow_color;
505     }
506     if (pw->panner.line_width > 0) {
507 	values.line_width = pw->panner.line_width;
508 	valuemask |= GCLineWidth;
509     }
510 
511     pw->panner.shadow_gc = XtGetGC ((Widget) pw, valuemask, &values);
512 }
513 
reset_slider_gc(pw)514 static void reset_slider_gc (pw)	/* used when resources change */
515     PannerWidget pw;
516 {
517     XtGCMask valuemask = GCForeground;
518     XGCValues values;
519 
520     if (pw->panner.slider_gc) XtReleaseGC ((Widget) pw, pw->panner.slider_gc);
521 
522     values.foreground = pw->panner.foreground;
523 
524     pw->panner.slider_gc = XtGetGC ((Widget) pw, valuemask, &values);
525 }
526 
reset_xor_gc(pw)527 static void reset_xor_gc (pw)		/* used when resources change */
528     PannerWidget pw;
529 {
530     if (pw->panner.xor_gc) XtReleaseGC ((Widget) pw, pw->panner.xor_gc);
531 
532     if (pw->panner.rubber_band) {
533 	XtGCMask valuemask = (GCForeground | GCFunction);
534 	XGCValues values;
535 	Pixel tmp;
536 
537 	tmp = ((pw->panner.foreground == pw->core.background_pixel) ?
538 	       pw->panner.shadow_color : pw->panner.foreground);
539 	values.foreground = tmp ^ pw->core.background_pixel;
540 	values.function = GXxor;
541 	if (pw->panner.line_width > 0) {
542 	    valuemask |= GCLineWidth;
543 	    values.line_width = pw->panner.line_width;
544 	}
545 	pw->panner.xor_gc = XtGetGC ((Widget) pw, valuemask, &values);
546     } else {
547 	pw->panner.xor_gc = NULL;
548     }
549 }
550 
551 
check_knob(pw,knob)552 static void check_knob (pw, knob)
553     PannerWidget pw;
554     Boolean knob;
555 {
556     Position pad = pw->panner.internal_border * 2;
557     Position maxx = (((Position) pw->core.width) - pad -
558 		     ((Position) pw->panner.knob_width));
559     Position maxy = (((Position) pw->core.height) - pad -
560 		     ((Position) pw->panner.knob_height));
561     Position *x = (knob ? &pw->panner.knob_x : &pw->panner.tmp.x);
562     Position *y = (knob ? &pw->panner.knob_y : &pw->panner.tmp.y);
563 
564     /*
565      * note that positions are already normalized (i.e. internal_border
566      * has been subtracted out)
567      */
568     if (*x < 0) *x = 0;
569     if (*x > maxx) *x = maxx;
570 
571     if (*y < 0) *y = 0;
572     if (*y > maxy) *y = maxy;
573 
574     if (knob) {
575 	pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) /
576 					  pw->panner.haspect + 0.5);
577 	pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) /
578 					  pw->panner.vaspect + 0.5);
579 	pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE;
580     }
581 }
582 
583 
move_shadow(pw)584 static void move_shadow (pw)
585     PannerWidget pw;
586 {
587     if (pw->panner.shadow_thickness > 0) {
588 	int lw = pw->panner.shadow_thickness + pw->panner.line_width * 2;
589 	int pad = pw->panner.internal_border;
590 
591 	if ((int)pw->panner.knob_height > lw && (int)pw->panner.knob_width > lw) {
592 	    XRectangle *r = pw->panner.shadow_rects;
593 	    r->x = (short) (pw->panner.knob_x + pad + pw->panner.knob_width);
594 	    r->y = (short) (pw->panner.knob_y + pad + lw);
595 	    r->width = pw->panner.shadow_thickness;
596 	    r->height = (unsigned short) (pw->panner.knob_height - lw);
597 	    r++;
598 	    r->x = (short) (pw->panner.knob_x + pad + lw);
599 	    r->y = (short) (pw->panner.knob_y + pad + pw->panner.knob_height);
600 	    r->width = (unsigned short) (pw->panner.knob_width - lw +
601 					 pw->panner.shadow_thickness);
602 	    r->height = pw->panner.shadow_thickness;
603 	    pw->panner.shadow_valid = TRUE;
604 	    return;
605 	}
606     }
607     pw->panner.shadow_valid = FALSE;
608 }
609 
scale_knob(pw,location,size)610 static void scale_knob (pw, location, size)  /* set knob size and/or loc */
611     PannerWidget pw;
612     Boolean location, size;
613 {
614     if (location) {
615 	pw->panner.knob_x = (Position) PANNER_HSCALE (pw, pw->panner.slider_x);
616 	pw->panner.knob_y = (Position) PANNER_VSCALE (pw, pw->panner.slider_y);
617     }
618     if (size) {
619 	Dimension width, height;
620 
621 	if (pw->panner.slider_width < 1) {
622 	    pw->panner.slider_width = pw->panner.canvas_width;
623 	}
624 	if (pw->panner.slider_height < 1) {
625 	    pw->panner.slider_height = pw->panner.canvas_height;
626 	}
627 	width = Min (pw->panner.slider_width, pw->panner.canvas_width);
628 	height = Min (pw->panner.slider_height, pw->panner.canvas_height);
629 
630 	pw->panner.knob_width = (Dimension) PANNER_HSCALE (pw, width);
631 	pw->panner.knob_height = (Dimension) PANNER_VSCALE (pw, height);
632     }
633     if (!pw->panner.allow_off) check_knob (pw, TRUE);
634     move_shadow (pw);
635 }
636 
rescale(pw)637 static void rescale (pw)
638     PannerWidget pw;
639 {
640     int hpad = pw->panner.internal_border * 2;
641     int vpad = hpad;
642 
643     if (pw->panner.canvas_width < 1)
644 	pw->panner.canvas_width = pw->core.width;
645     if (pw->panner.canvas_height < 1)
646 	pw->panner.canvas_height = pw->core.height;
647 
648     if ((int)pw->core.width <= hpad) hpad = 0;
649     if ((int)pw->core.height <= vpad) vpad = 0;
650 
651     pw->panner.haspect = ((double) pw->core.width - hpad) /
652 	(double) pw->panner.canvas_width;
653     pw->panner.vaspect = ((double) pw->core.height - vpad) /
654 	(double) pw->panner.canvas_height;
655     scale_knob (pw, TRUE, TRUE);
656 }
657 
658 
get_default_size(pw,wp,hp)659 static void get_default_size (pw, wp, hp)
660     PannerWidget pw;
661     Dimension *wp, *hp;
662 {
663     Dimension pad = pw->panner.internal_border * 2;
664     *wp = PANNER_DSCALE (pw, pw->panner.canvas_width) + pad;
665     *hp = PANNER_DSCALE (pw, pw->panner.canvas_height) + pad;
666 }
667 
get_event_xy(pw,event,x,y)668 static Boolean get_event_xy (pw, event, x, y)
669     PannerWidget pw;
670     XEvent *event;
671     int *x, *y;
672 {
673     int pad = pw->panner.internal_border;
674 
675     switch (event->type) {
676     case ButtonPress:
677     case ButtonRelease:
678 	*x = event->xbutton.x - pad;
679 	*y = event->xbutton.y - pad;
680 	return TRUE;
681 
682     case KeyPress:
683     case KeyRelease:
684 	*x = event->xkey.x - pad;
685 	*y = event->xkey.y - pad;
686 	return TRUE;
687 
688     case EnterNotify:
689     case LeaveNotify:
690 	*x = event->xcrossing.x - pad;
691 	*y = event->xcrossing.y - pad;
692 	return TRUE;
693 
694     case MotionNotify:
695 	*x = event->xmotion.x - pad;
696 	*y = event->xmotion.y - pad;
697 	return TRUE;
698     }
699 
700     return FALSE;
701 }
702 
parse_page_string(s,pagesize,canvassize,relative)703 static int parse_page_string (s, pagesize, canvassize, relative)
704     char *s;
705     int pagesize, canvassize;
706     Boolean *relative;
707 {
708     char *cp;
709     double val = 1.0;
710     Boolean rel = FALSE;
711 
712     /*
713      * syntax:    spaces [+-] number spaces [pc\0] spaces
714      */
715 
716     for (; isascii(*s) && isspace(*s); s++) ;	/* skip white space */
717 
718     if (*s == '+' || *s == '-') {	/* deal with signs */
719 	rel = TRUE;
720 	if (*s == '-') val = -1.0;
721 	s++;
722     }
723     if (!*s) {				/* if null then return nothing */
724 	*relative = TRUE;
725 	return 0;
726     }
727 
728     /* skip over numbers */
729     for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++) ;
730     val *= atof(cp);
731 
732     /* skip blanks */
733     for (; isascii(*s) && isspace(*s); s++) ;
734 
735     if (*s) {				/* if units */
736 	switch (s[0]) {
737 	case 'p': case 'P':
738 	    val *= (double) pagesize;
739 	    break;
740 
741 	case 'c': case 'C':
742 	    val *= (double) canvassize;
743 	    break;
744 	}
745     }
746     *relative = rel;
747     return ((int) val);
748 }
749 
750 
751 #define DRAW_TMP(pw) \
752 { \
753     XDrawRectangle (XtDisplay(pw), XtWindow(pw), \
754 		    pw->panner.xor_gc, \
755 		    (int) (pw->panner.tmp.x + pw->panner.internal_border), \
756 		    (int) (pw->panner.tmp.y + pw->panner.internal_border), \
757 		    (unsigned int) (pw->panner.knob_width - 1), \
758 		    (unsigned int) (pw->panner.knob_height - 1)); \
759     pw->panner.tmp.showing = !pw->panner.tmp.showing; \
760 }
761 
762 #define UNDRAW_TMP(pw) \
763 { \
764     if (pw->panner.tmp.showing) DRAW_TMP(pw); \
765 }
766 
767 #define BACKGROUND_STIPPLE(pw) \
768   XmuLocatePixmapFile (pw->core.screen, pw->panner.stipple_name, \
769 		       pw->panner.shadow_color, pw->core.background_pixel, \
770 		       pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL)
771 
772 #define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap)
773 
774 
775 /*****************************************************************************
776  *                                                                           *
777  * 			     panner class methods                            *
778  *                                                                           *
779  *****************************************************************************/
780 
781 
782 /*ARGSUSED*/
Initialize(greq,gnew,args,num_args)783 static void Initialize (greq, gnew, args, num_args)
784     Widget greq, gnew;
785     ArgList args;
786     Cardinal *num_args;
787 {
788     PannerWidget req = (PannerWidget) greq, new = (PannerWidget) gnew;
789     Dimension defwidth, defheight;
790 
791     UNUSED(args);
792     UNUSED(num_args);
793 
794     if (req->panner.canvas_width < 1) new->panner.canvas_width = 1;
795     if (req->panner.canvas_height < 1) new->panner.canvas_height = 1;
796     if (req->panner.default_scale < 1)
797 	new->panner.default_scale = PANNER_DEFAULT_SCALE;
798 
799     get_default_size (req, &defwidth, &defheight);
800     if (req->core.width < 1) new->core.width = defwidth;
801     if (req->core.height < 1) new->core.height = defheight;
802 
803     new->panner.shadow_gc = NULL;
804     reset_shadow_gc (new);		/* shadowColor */
805     new->panner.slider_gc = NULL;
806     reset_slider_gc (new);		/* foreground */
807     new->panner.xor_gc = NULL;
808     reset_xor_gc (new);			/* foreground ^ background */
809 
810     rescale (new);			/* does a position check */
811     new->panner.shadow_valid = FALSE;
812     new->panner.tmp.doing = FALSE;
813     new->panner.tmp.showing = FALSE;
814 }
815 
816 
PannerRealize(gw,valuemaskp,attr)817 static void PannerRealize (gw, valuemaskp, attr)
818     Widget gw;
819     XtValueMask *valuemaskp;
820     XSetWindowAttributes *attr;
821 {
822     PannerWidget pw = (PannerWidget) gw;
823     Pixmap pm = XtUnspecifiedPixmap;
824     Boolean gotpm = FALSE;
825 
826     if (pw->core.background_pixmap == XtUnspecifiedPixmap) {
827 	if (pw->panner.stipple_name) pm = BACKGROUND_STIPPLE (pw);
828 
829 	if (PIXMAP_OKAY(pm)) {
830 	    attr->background_pixmap = pm;
831 	    *valuemaskp |= CWBackPixmap;
832 	    *valuemaskp &= ~CWBackPixel;
833 	    gotpm = TRUE;
834 	}
835     }
836     (*pannerWidgetClass->core_class.superclass->core_class.realize)
837 	(gw, valuemaskp, attr);
838 
839     if (gotpm) XFreePixmap (XtDisplay(gw), pm);
840 }
841 
842 
Destroy(gw)843 static void Destroy (gw)
844     Widget gw;
845 {
846     PannerWidget pw = (PannerWidget) gw;
847 
848     XtReleaseGC (gw, pw->panner.shadow_gc);
849     XtReleaseGC (gw, pw->panner.slider_gc);
850     XtReleaseGC (gw, pw->panner.xor_gc);
851 }
852 
853 
Resize(gw)854 static void Resize (gw)
855     Widget gw;
856 {
857     rescale ((PannerWidget) gw);
858 }
859 
860 
861 /* ARGSUSED */
Redisplay(Widget gw,XEvent * event,Region region)862 static void Redisplay (Widget gw, XEvent *event, Region region)
863 {
864     PannerWidget pw = (PannerWidget) gw;
865     Display *dpy = XtDisplay(gw);
866     Window w = XtWindow(gw);
867     int pad = pw->panner.internal_border;
868     Dimension lw = pw->panner.line_width;
869     Dimension extra = pw->panner.shadow_thickness + lw * 2;
870     int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad;
871 
872     UNUSED(event);
873     UNUSED(region);
874 
875     pw->panner.tmp.showing = FALSE;
876     XClearArea (XtDisplay(pw), XtWindow(pw),
877 		(int) pw->panner.last_x - ((int) lw) + pad,
878 		(int) pw->panner.last_y - ((int) lw) + pad,
879 		(unsigned int) (pw->panner.knob_width + extra),
880 		(unsigned int) (pw->panner.knob_height + extra),
881 		False);
882     pw->panner.last_x = pw->panner.knob_x;
883     pw->panner.last_y = pw->panner.knob_y;
884 
885     XFillRectangle (dpy, w, pw->panner.slider_gc, kx, ky,
886 		    pw->panner.knob_width - 1, pw->panner.knob_height - 1);
887 
888     if (lw)
889     {
890     	XDrawRectangle (dpy, w, pw->panner.shadow_gc, kx, ky,
891 		    	(unsigned int) (pw->panner.knob_width - 1),
892 		    	(unsigned int) (pw->panner.knob_height - 1));
893     }
894 
895     if (pw->panner.shadow_valid) {
896 	XFillRectangles (dpy, w, pw->panner.shadow_gc,
897 			 pw->panner.shadow_rects, 2);
898     }
899     if (pw->panner.tmp.doing && pw->panner.rubber_band) DRAW_TMP (pw);
900 }
901 
902 
903 /* ARGSUSED */
PannerSetValues(Widget gcur,Widget greq,Widget gnew,ArgList args,Cardinal * num_args)904 static Boolean PannerSetValues (Widget gcur, Widget greq, Widget gnew, ArgList args, Cardinal *num_args)
905 {
906     PannerWidget cur = (PannerWidget) gcur;
907     PannerWidget new = (PannerWidget) gnew;
908     Boolean redisplay = FALSE;
909 
910     UNUSED(greq);
911     UNUSED(args);
912     UNUSED(num_args);
913 
914     if (cur->panner.foreground != new->panner.foreground) {
915 	reset_slider_gc (new);
916 	if (cur->panner.foreground != cur->core.background_pixel)
917 	    reset_xor_gc (new);
918 	redisplay = TRUE;
919     } else if (cur->panner.line_width != new->panner.line_width ||
920 	       cur->core.background_pixel != new->core.background_pixel) {
921 	reset_xor_gc (new);
922 	redisplay = TRUE;
923     }
924     if (cur->panner.shadow_color != new->panner.shadow_color) {
925 	reset_shadow_gc (new);
926 	if (cur->panner.foreground == cur->core.background_pixel)
927 	    reset_xor_gc (new);
928 	redisplay = TRUE;
929     }
930     if (cur->panner.shadow_thickness != new->panner.shadow_thickness) {
931 	move_shadow (new);
932 	redisplay = TRUE;
933     }
934     if (cur->panner.rubber_band != new->panner.rubber_band) {
935 	reset_xor_gc (new);
936 	if (new->panner.tmp.doing) redisplay = TRUE;
937     }
938 
939     if ((cur->panner.stipple_name != new->panner.stipple_name ||
940 	 cur->panner.shadow_color != new->panner.shadow_color ||
941 	 cur->core.background_pixel != new->core.background_pixel) &&
942 	XtIsRealized(gnew)) {
943 	Pixmap pm = (new->panner.stipple_name ? BACKGROUND_STIPPLE (new)
944 		     : XtUnspecifiedPixmap);
945 
946 	if (PIXMAP_OKAY(pm)) {
947 	    XSetWindowBackgroundPixmap (XtDisplay (new), XtWindow(new), pm);
948 	    XFreePixmap (XtDisplay (new), pm);
949 	} else {
950 	    XSetWindowBackground (XtDisplay (new), XtWindow(new),
951 				  new->core.background_pixel);
952 	}
953 	redisplay = TRUE;
954     }
955 
956     if (new->panner.resize_to_pref &&
957 	(cur->panner.canvas_width != new->panner.canvas_width ||
958 	 cur->panner.canvas_height != new->panner.canvas_height ||
959 	 cur->panner.resize_to_pref != new->panner.resize_to_pref)) {
960 	get_default_size (new, &new->core.width, &new->core.height);
961 	redisplay = TRUE;
962     } else if (cur->panner.canvas_width != new->panner.canvas_width ||
963 	       cur->panner.canvas_height != new->panner.canvas_height ||
964 	       cur->panner.internal_border != new->panner.internal_border) {
965 	rescale (new);			/* does a scale_knob as well */
966 	redisplay = TRUE;
967     } else {
968 	Boolean loc = (cur->panner.slider_x != new->panner.slider_x ||
969 		       cur->panner.slider_y != new->panner.slider_y);
970 	Boolean siz = (cur->panner.slider_width != new->panner.slider_width ||
971 		       cur->panner.slider_height != new->panner.slider_height);
972 	if (loc || siz ||
973 	    (cur->panner.allow_off != new->panner.allow_off &&
974 	     new->panner.allow_off)) {
975 	    scale_knob (new, loc, siz);
976 	    redisplay = TRUE;
977 	}
978     }
979 
980     return redisplay;
981 }
982 
SetValuesAlmost(gold,gnew,req,reply)983 static void SetValuesAlmost (gold, gnew, req, reply)
984     Widget gold, gnew;
985     XtWidgetGeometry *req, *reply;
986 {
987     if (reply->request_mode == 0) {	/* got turned down, so cope */
988 	Resize (gnew);
989     }
990     (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost)
991 	(gold, gnew, req, reply);
992 }
993 
QueryGeometry(gw,intended,pref)994 static XtGeometryResult QueryGeometry (gw, intended, pref)
995     Widget gw;
996     XtWidgetGeometry *intended, *pref;
997 {
998     PannerWidget pw = (PannerWidget) gw;
999 
1000     pref->request_mode = (CWWidth | CWHeight);
1001     get_default_size (pw, &pref->width, &pref->height);
1002 
1003     if (((intended->request_mode & (CWWidth | CWHeight)) ==
1004 	 (CWWidth | CWHeight)) &&
1005 	intended->width == pref->width &&
1006 	intended->height == pref->height)
1007 	return XtGeometryYes;
1008     else if (pref->width == pw->core.width && pref->height == pw->core.height)
1009 	return XtGeometryNo;
1010     else
1011 	return XtGeometryAlmost;
1012 }
1013 
1014 
1015 /*****************************************************************************
1016  *                                                                           *
1017  * 			      panner action procs                            *
1018  *                                                                           *
1019  *****************************************************************************/
1020 
1021 /* ARGSUSED */
ActionStart(Widget gw,XEvent * event,String * params,Cardinal * num_params)1022 static void ActionStart (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1023 {
1024     PannerWidget pw = (PannerWidget) gw;
1025     int x, y;
1026 
1027     UNUSED(params);
1028     UNUSED(num_params);
1029 
1030     if (!get_event_xy (pw, event, &x, &y)) {
1031 	panBell(XtDisplay(gw), XtWindow(gw), 0);   /* should do error message */
1032 	return;
1033     }
1034 
1035     pw->panner.tmp.doing = TRUE;
1036     pw->panner.tmp.startx = pw->panner.knob_x;
1037     pw->panner.tmp.starty = pw->panner.knob_y;
1038     pw->panner.tmp.dx = (((Position) x) - pw->panner.knob_x);
1039     pw->panner.tmp.dy = (((Position) y) - pw->panner.knob_y);
1040     pw->panner.tmp.x = pw->panner.knob_x;
1041     pw->panner.tmp.y = pw->panner.knob_y;
1042     if (pw->panner.rubber_band) DRAW_TMP (pw);
1043 }
1044 
1045 /* ARGSUSED */
ActionStop(Widget gw,XEvent * event,String * params,Cardinal * num_params)1046 static void ActionStop (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1047 {
1048     PannerWidget pw = (PannerWidget) gw;
1049     int x, y;
1050 
1051     UNUSED(params);
1052     UNUSED(num_params);
1053 
1054     if (get_event_xy (pw, event, &x, &y)) {
1055 	pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx;
1056 	pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy;
1057 	if (!pw->panner.allow_off) check_knob (pw, FALSE);
1058     }
1059     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
1060     pw->panner.tmp.doing = FALSE;
1061 }
1062 
1063 /* ARGSUSED */
ActionAbort(Widget gw,XEvent * event,String * params,Cardinal * num_params)1064 static void ActionAbort (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1065 {
1066     PannerWidget pw = (PannerWidget) gw;
1067 
1068     UNUSED(params);
1069     UNUSED(num_params);
1070 
1071     if (!pw->panner.tmp.doing) return;
1072 
1073     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
1074 
1075     if (!pw->panner.rubber_band) {		/* restore old position */
1076 	pw->panner.tmp.x = pw->panner.tmp.startx;
1077 	pw->panner.tmp.y = pw->panner.tmp.starty;
1078 	ActionNotify (gw, event, params, num_params);
1079     }
1080     pw->panner.tmp.doing = FALSE;
1081 }
1082 
1083 
1084 /* ARGSUSED */
ActionMove(Widget gw,XEvent * event,String * params,Cardinal * num_params)1085 static void ActionMove (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1086 {
1087     PannerWidget pw = (PannerWidget) gw;
1088     int x, y;
1089 
1090     UNUSED(params);
1091     UNUSED(num_params);
1092 
1093     if (!pw->panner.tmp.doing) return;
1094 
1095     if (!get_event_xy (pw, event, &x, &y)) {
1096 	panBell(XtDisplay(gw), XtWindow(gw), 0);   /* should do error message */
1097 	return;
1098     }
1099 
1100     if (pw->panner.rubber_band) UNDRAW_TMP (pw);
1101     pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx;
1102     pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy;
1103 
1104     if (!pw->panner.rubber_band) {
1105 	ActionNotify (gw, event, params, num_params);  /* does a check */
1106     } else {
1107 	if (!pw->panner.allow_off) check_knob (pw, FALSE);
1108 	DRAW_TMP (pw);
1109     }
1110 }
1111 
1112 
1113 /* ARGSUSED */
ActionPage(Widget gw,XEvent * event,String * params,Cardinal * num_params)1114 static void ActionPage (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1115 {
1116     PannerWidget pw = (PannerWidget) gw;
1117     Cardinal zero = 0;
1118     Boolean isin = pw->panner.tmp.doing;
1119     int x, y;
1120     Boolean relx, rely;
1121     int pad = pw->panner.internal_border * 2;
1122 
1123     UNUSED(event);
1124     UNUSED(num_params);
1125 
1126     if (*num_params != 2) {
1127 	panBell (XtDisplay(gw), XtWindow(gw), 0);
1128 	return;
1129     }
1130 
1131     x = parse_page_string (params[0], (int) pw->panner.knob_width,
1132 			   ((int) pw->core.width) - pad, &relx);
1133     y = parse_page_string (params[1], (int) pw->panner.knob_height,
1134 			   ((int) pw->core.height) - pad, &rely);
1135 
1136     if (relx) x += pw->panner.knob_x;
1137     if (rely) y += pw->panner.knob_y;
1138 
1139     if (isin) {				/* if in, then use move */
1140 	XEvent ev;
1141 	ev.xbutton.type = ButtonPress;
1142 	ev.xbutton.x = x;
1143 	ev.xbutton.y = y;
1144 	ActionMove (gw, &ev, (String *) NULL, &zero);
1145     } else {				/* else just do it */
1146 	pw->panner.tmp.doing = TRUE;
1147 	pw->panner.tmp.x = x;
1148 	pw->panner.tmp.y = y;
1149 	ActionNotify (gw, event, (String *) NULL, &zero);
1150 	pw->panner.tmp.doing = FALSE;
1151     }
1152 }
1153 
1154 
1155 /* ARGSUSED */
ActionNotify(Widget gw,XEvent * event,String * params,Cardinal * num_params)1156 static void ActionNotify (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1157 {
1158     PannerWidget pw = (PannerWidget) gw;
1159 
1160     UNUSED(event);
1161     UNUSED(params);
1162     UNUSED(num_params);
1163 
1164     if (!pw->panner.tmp.doing) return;
1165 
1166     if (!pw->panner.allow_off) check_knob (pw, FALSE);
1167     pw->panner.knob_x = pw->panner.tmp.x;
1168     pw->panner.knob_y = pw->panner.tmp.y;
1169     move_shadow (pw);
1170 
1171     pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) /
1172 				      pw->panner.haspect + 0.5);
1173     pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) /
1174 				      pw->panner.vaspect + 0.5);
1175     if (!pw->panner.allow_off) {
1176 	Position tmp;
1177 
1178 	if (pw->panner.slider_x >
1179 	    (tmp = (((Position) pw->panner.canvas_width) -
1180 		    ((Position) pw->panner.slider_width))))
1181 	    pw->panner.slider_x = tmp;
1182 	if (pw->panner.slider_x < 0) pw->panner.slider_x = 0;
1183 	if (pw->panner.slider_y >
1184 	    (tmp = (((Position) pw->panner.canvas_height) -
1185 		    ((Position) pw->panner.slider_height))))
1186 	    pw->panner.slider_y = tmp;
1187 	if (pw->panner.slider_y < 0) pw->panner.slider_y = 0;
1188     }
1189 
1190     if (pw->panner.last_x != pw->panner.knob_x ||
1191 	pw->panner.last_y != pw->panner.knob_y) {
1192 	XawPannerReport rep;
1193 
1194 	Redisplay (gw, (XEvent*) NULL, (Region) NULL);
1195 	rep.changed = (XawPRSliderX | XawPRSliderY);
1196 	rep.slider_x = pw->panner.slider_x;
1197 	rep.slider_y = pw->panner.slider_y;
1198 	rep.slider_width = pw->panner.slider_width;
1199 	rep.slider_height = pw->panner.slider_height;
1200 	rep.canvas_width = pw->panner.canvas_width;
1201 	rep.canvas_height = pw->panner.canvas_height;
1202 	XtCallCallbackList (gw, pw->panner.report_callbacks, (XtPointer) &rep);
1203     }
1204 }
1205 
1206 /* ARGSUSED */
ActionSet(Widget gw,XEvent * event,String * params,Cardinal * num_params)1207 static void ActionSet (Widget gw, XEvent *event, String *params, Cardinal *num_params)
1208 {
1209     PannerWidget pw = (PannerWidget) gw;
1210     Boolean rb;
1211 
1212     UNUSED(event);
1213 
1214     if (*num_params < 2 ||
1215 	XmuCompareISOLatin1 (params[0], "rubberband") != 0) {
1216 	panBell (XtDisplay(gw), XtWindow(gw), 0);
1217 	return;
1218     }
1219 
1220     if (XmuCompareISOLatin1 (params[1], "on") == 0) {
1221 	rb = TRUE;
1222     } else if (XmuCompareISOLatin1 (params[1], "off") == 0) {
1223 	rb = FALSE;
1224     } else if (XmuCompareISOLatin1 (params[1], "toggle") == 0) {
1225 	rb = !pw->panner.rubber_band;
1226     } else {
1227 	panBell (XtDisplay(gw), XtWindow(gw), 0);
1228 	return;
1229     }
1230 
1231     if (rb != pw->panner.rubber_band) {
1232 	Arg args[1];
1233 	XtSetArg (args[0], XtNrubberBand, rb);
1234 	XtSetValues (gw, args, (Cardinal) 1);
1235     }
1236 }
1237 
1238 #endif /* MOTIF && USE_PANNER */
1239