1 /* MwSlider.c -- Slider widget
2  *
3  * Author: Edward A. Falk
4  *	   falk@falconer.vip.best.com
5  *
6  * Date: 2 Nov 1999
7  *
8  * Copyright 1999 by Edward A. Falk.  This widget may be used freely in any
9  * software.  Source code is freely distributable provided that my name
10  * is not removed from it.
11  *
12  * The X Consortium, and any party obtaining a copy of these files
13  * from the X Consortium, directly or indirectly, is granted, free of
14  * charge, a full and unrestricted irrevocable, world-wide, paid up,
15  * royalty-free, nonexclusive right and license to deal in this
16  * software and documentation files (the "Software"), including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sublicense, and sell copies of the Software, and to
19  * permit persons who receive copies from any such party to do so.
20  *
21  *
22  *
23  * This widget presents the user with a graphical slider which the
24  * user may manipulate to set a numeric value.
25  *
26  * $Log: Slider.c,v $
27  * Revision 1.7  2000/01/19 01:46:09  falk
28  * Fixed resources: input scale is short, not int.
29  *
30  * Revision 1.6  2000/01/18 20:39:24  falk
31  * _SliderThumb() utility added.  Other code simplified accordingly.
32  *
33  * Revision 1.5  2000/01/17 20:54:21  falk
34  * default now horizontal.  PreferredSize now a class method.  DrawThumb
35  * now centers properly.
36  *
37  * Revision 1.4  2000/01/14 19:04:39  falk
38  * fixed polarity of scrollwheel mouse interactions
39  *
40  * Revision 1.3  2000/01/11 19:39:36  falk
41  * Added foreground resource.  Added keyboard focus.
42  *
43  * Revision 1.2  2000/01/11 18:26:28  falk
44  * Recomputes slider position after resize.  Added "newvalue" argument
45  * to XawSliderSetValue
46  *
47  * Revision 1.1  1999/11/04 17:22:10  falk
48  * Initial revision
49  *
50  *
51  */
52 
53 #include	<stdio.h>
54 #include	<stdlib.h>
55 #include	<ctype.h>
56 
57 #include	<X11/Xlib.h>
58 #include	<X11/IntrinsicP.h>
59 #include	<X11/StringDefs.h>
60 #include	<X11/Xmu/Drawing.h>
61 #include	<X11/Xmu/Misc.h>
62 #include	<X11/Xmu/CharSet.h>
63 
64 #include	"MwSliderP.h"
65 #include	"MwGcs.h"
66 
67 #define	DEF_WID		100
68 #define	DEF_HGT		10
69 #define	THUMB_WID	20	/* default thumb width */
70 #define	THUMB_HGT	10	/* default thumb height */
71 
72 
73 /****************************************************************
74  *
75  * MwSlider Resources
76  *
77  ****************************************************************/
78 
79 #define	offset(field)	XtOffsetOf(MwSliderRec, slider.field)
80 static XtResource resources[] = {
81   {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
82 	offset(font), XtRString, XtDefaultFont},
83   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
84 	offset(foreground), XtRString, XtDefaultForeground},
85   {XtNminimum, XtCMinimum, XtRInt, sizeof(int),
86 	offset(minimum), XtRImmediate, (XtPointer)0},
87   {XtNmaximum, XtCMaximum, XtRInt, sizeof(int),
88 	offset(maximum), XtRImmediate, (XtPointer)100},
89   {XtNvalue, XtCValue, XtRInt, sizeof(int),
90 	offset(value), XtRImmediate, (XtPointer)0},
91   {XtNstep, XtCStep, XtRInt, sizeof(int),
92 	offset(step), XtRImmediate, (XtPointer)-1},
93   {XtNstep2, XtCStep2, XtRInt, sizeof(int),
94 	offset(step2), XtRImmediate, (XtPointer)1},
95   {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
96 	offset(orientation), XtRImmediate, (XtPointer) XtorientHorizontal},
97   {XtNshowValue, XtCShowValue, XtRBoolean, sizeof(Boolean),
98 	offset(showValue), XtRImmediate, (XtPointer) False},
99   {XtNdecimalPoints, XtCDecimalPoints, XtRShort, sizeof(short),
100 	offset(decimalPoints), XtRImmediate, (XtPointer)0},
101   {XtNinputScale, XtCInputScale, XtRShort, sizeof(short),
102 	offset(inputScale), XtRImmediate, (XtPointer)100},
103   {XtNautoScale, XtCAutoScale, XtRBoolean, sizeof(Boolean),
104 	offset(autoScale), XtRImmediate, (XtPointer) True},
105   {XtNthumbLength, XtCThumbLength, XtRShort, sizeof(short),
106 	offset(thumbLength), XtRImmediate, (XtPointer) THUMB_WID},
107   {XtNdragCallback, XtCDragCallback, XtRCallback, sizeof(XtPointer),
108 	offset(dragCallback), XtRCallback, NULL},
109   {XtNvalueChangedCallback, XtCValueChangedCallback,
110   	XtRCallback, sizeof(XtPointer),
111 	offset(valueChangedCallback), XtRCallback, NULL},
112 
113   {XtNbeNiceToColormap, XtCBeNiceToColormap, XtRBoolean, sizeof(Boolean),
114 	offset(be_nice_to_cmap), XtRImmediate, (XtPointer) False},
115   {XtNshadowWidth, XtCShadowWidth, XtRShort, sizeof(short),
116 	offset(shadowWidth), XtRImmediate, (XtPointer) 1},
117   {XtNtopShadowContrast, XtCTopShadowContrast, XtRShort, sizeof(short),
118 	offset(top_contrast), XtRImmediate, (XtPointer) 20},
119   {XtNbottomShadowContrast, XtCBottomShadowContrast, XtRShort, sizeof(short),
120 	offset(bot_contrast), XtRImmediate, (XtPointer) 40},
121   {XtNarmShadowContrast, XtCArmShadowContrast, XtRShort, sizeof(short),
122 	offset(arm_contrast), XtRImmediate, (XtPointer) 20},
123   {XtNinsensitiveContrast, XtCInsensitiveContrast, XtRShort, sizeof(short),
124 	offset(insensitive_contrast), XtRImmediate, (XtPointer) 33},
125 };
126 #undef	offset
127 
128 
129 
130 
131 
132 
133 	/* FORWARD REFERENCES: */
134 
135 #ifdef	NeedFunctionPrototypes
136 
137 	/* member functions */
138 
139 static	void	SliderClassInit() ;
140 static	void	SliderClassPartInit(WidgetClass wc) ;
141 static	void	SliderInit( Widget req, Widget new, ArgList, Cardinal *nargs) ;
142 static	void	SliderRealize(Widget, Mask *, XSetWindowAttributes *) ;
143 static	void	SliderDestroy( Widget w) ;
144 static	void	SliderResize( Widget w) ;
145 static	void	SliderExpose( Widget w, XEvent *event, Region region) ;
146 static	Boolean	SliderSetValues(Widget, Widget, Widget, ArgList, Cardinal *) ;
147 static	XtGeometryResult
148 	  SliderQueryGeometry( Widget, XtWidgetGeometry *, XtWidgetGeometry *) ;
149 static	Boolean	SliderAcceptFocus(Widget, Time *) ;
150 static	void	SliderDrawBorder(MwSliderWidget) ;
151 static	void	SliderDrawBackground(MwSliderWidget, int, int, int, int) ;
152 static	void	SliderDrawThumb(MwSliderWidget) ;
153 static	void	SliderMoveThumb(MwSliderWidget sw, int newpos) ;
154 static	void	SliderFocusProc(MwSliderWidget sw) ;
155 
156 	/* action procs */
157 
158 static	void	StartAdjust(Widget, XEvent *, String *, Cardinal *) ;
159 static	void	HandleThumb(Widget, XEvent *, String *, Cardinal *) ;
160 static	void	EndAdjust(Widget, XEvent *, String *, Cardinal *) ;
161 static	void	Adjust(Widget, XEvent *, String *, Cardinal *) ;
162 static	void	Notify(Widget, XEvent *, String *, Cardinal *) ;
163 static	void	Focus(Widget, XEvent *, String *, Cardinal *) ;
164 
165 
166 	/* internal privates */
167 
168 static	void	ChangeSliderValue(MwSliderWidget sw, int value) ;
169 
170 static	void	SliderPreferredSize( MwSliderWidget,
171 			Dimension *wid, Dimension *hgt,
172 			Dimension *min_wid, Dimension *min_hgt) ;
173 
174 static	void	SliderAllocGCs(MwSliderWidget) ;
175 static	void	SliderFreeGCs(MwSliderWidget) ;
176 
177 #else
178 
179 static	void	SliderClassInit() ;
180 static	void	SliderClassPartInit() ;
181 static	void	SliderInit() ;
182 static	void	SliderRealize() ;
183 static	void	SliderDestroy() ;
184 static	void	SliderResize() ;
185 static	void	SliderExpose() ;
186 static	Boolean	SliderSetValues() ;
187 static	XtGeometryResult SliderQueryGeometry() ;
188 static	Boolean	SliderAcceptFocus() ;
189 static	void	SliderDrawBorder() ;
190 static	void	SliderDrawBackground() ;
191 static	void	SliderDrawThumb() ;
192 static	void	SliderMoveThumb() ;
193 static	void	SliderFocusProc() ;
194 static	void	StartAdjust() ;
195 static	void	HandleThumb() ;
196 static	void	EndAdjust() ;
197 static	void	Adjust() ;
198 static	void	Notify() ;
199 static	void	Focus() ;
200 static	void	ChangeSliderValue() ;
201 static	void	SliderPreferredSize() ;
202 static	void	SliderAllocGCs() ;
203 static	void	SliderFreeGCs() ;
204 
205 #endif
206 
207 
208 static	char	defaultTranslations[] = "\
209 	<FocusIn>:		Focus(1)		\n\
210 	<FocusOut>:		Focus(0)		\n\
211 	<Btn1Down>:		StartAdjust()		\n\
212 	<Btn1Motion>:		HandleThumb()		\n\
213 	<Btn1Up>:		EndAdjust() Notify()	\n\
214 	<Btn4Down>:		Adjust(step2) Notify()	\n\
215 	<Btn5Down>:		Adjust(-step2) Notify()	\n\
216 	<Key>Left:		Adjust(-step2) Notify()	\n\
217 	<Key>KP_Left:		Adjust(-step2) Notify()	\n\
218 	<Key>Right:		Adjust(step2) Notify()	\n\
219 	<Key>KP_Right:		Adjust(step2) Notify()	\n\
220 	<Key>Down:		Adjust(-step2) Notify()	\n\
221 	<Key>KP_Down:		Adjust(-step2) Notify()	\n\
222 	<Key>Up:		Adjust(step2) Notify()	\n\
223 	<Key>KP_Up:		Adjust(step2) Notify()	\n\
224 	<Key>Page_Up:		Adjust(step) Notify()	\n\
225 	<Key>KP_Page_Up:	Adjust(step) Notify()	\n\
226 	<Key>Page_Down:		Adjust(-step) Notify()	\n\
227 	<Key>KP_Page_Down:	Adjust(-step) Notify()	\n\
228 	<Key>Home:		Adjust(home) Notify()	\n\
229 	<Key>KP_Home:		Adjust(home) Notify()	\n\
230 	<Key>End:		Adjust(end) Notify()	\n\
231 	<Key>KP_End:		Adjust(end) Notify()	\n\
232 	 " ;
233 
234 
235 static	XtActionsRec	actionsList[] =
236   {
237     {"StartAdjust",	StartAdjust},
238     {"HandleThumb",	HandleThumb},
239     {"EndAdjust",	EndAdjust},
240     {"Adjust",		Adjust},
241     {"Notify",		Notify},
242     {"Focus",		Focus},
243   } ;
244 
245 
246 
247 /****************************************************************
248 *
249 * Full class record constant
250 *
251 ****************************************************************/
252 
253 #define	SuperClass	(&coreClassRec)
254 
255 MwSliderClassRec mwSliderClassRec = {
256   {
257 /* core_class fields      */
258     /* superclass         */    (WidgetClass) SuperClass,
259     /* class_name         */    "MwSlider",
260     /* widget_size        */    sizeof(MwSliderRec),
261     /* class_initialize   */    SliderClassInit,
262     /* class_part_init    */	SliderClassPartInit,
263     /* class_inited       */	FALSE,
264     /* initialize         */    SliderInit,
265     /* initialize_hook    */	NULL,
266     /* realize            */    SliderRealize,
267     /* actions            */    actionsList,
268     /* num_actions	  */	XtNumber(actionsList),
269     /* resources          */    resources,
270     /* num_resources      */    XtNumber(resources),
271     /* xrm_class          */    NULLQUARK,
272     /* compress_motion	  */	TRUE,
273     /* compress_exposure  */	XtExposeCompressMaximal|
274 				XtExposeGraphicsExpose|
275     				XtExposeGraphicsExposeMerged|
276 				XtExposeNoRegion,
277     /* compress_enterleave*/	TRUE,
278     /* visible_interest   */    FALSE,
279     /* destroy            */    SliderDestroy,
280     /* resize             */    SliderResize,
281     /* expose             */    SliderExpose,
282     /* set_values         */    SliderSetValues,
283     /* set_values_hook    */	NULL,
284     /* set_values_almost  */    XtInheritSetValuesAlmost,
285     /* get_values_hook    */	NULL,
286     /* accept_focus       */    SliderAcceptFocus,
287     /* version            */	XtVersion,
288     /* callback_private   */    NULL,
289     /* tm_table           */    defaultTranslations,
290     /* query_geometry     */	SliderQueryGeometry,
291     /* display_accelerator*/	XtInheritDisplayAccelerator,
292     /* extension          */	NULL
293   },
294   {
295 /* Slider class fields */
296     /* draw_border	*/	SliderDrawBorder,
297     /* draw_background	*/	SliderDrawBackground,
298     /* draw_thumb	*/	SliderDrawThumb,
299     /* move_thumb	*/	SliderMoveThumb,
300     /* focus_proc	*/	SliderFocusProc,
301     /* preferred_size	*/	SliderPreferredSize,
302     /* extension	*/	NULL,
303   }
304 };
305 
306 WidgetClass mwSliderWidgetClass = (WidgetClass)&mwSliderClassRec;
307 
308 
309 
310 #ifdef	DEBUG
311 #ifdef	__STDC__
312 #define	assert(e) \
313 	  if(!(e)) fprintf(stderr,"yak! %s at %s:%d\n",#e,__FILE__,__LINE__)
314 #else
315 #define	assert(e) \
316 	  if(!(e)) fprintf(stderr,"yak! e at %s:%d\n",__FILE__,__LINE__)
317 #endif
318 #else
319 #define	assert(e)
320 #endif
321 
322 
323 
324 
325 /****************************************************************
326  *
327  * Member Procedures
328  *
329  ****************************************************************/
330 
331 static void
SliderClassInit(void)332 SliderClassInit(void)
333 {
334     /* register converters */
335 
336     XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
337     	NULL, 0) ;
338 }
339 
340 
341 static	void
SliderClassPartInit(WidgetClass wc)342 SliderClassPartInit(WidgetClass wc)
343 {
344   MwSliderWidgetClass sc = (MwSliderWidgetClass)wc ;
345   MwSliderWidgetClass super = (MwSliderWidgetClass)wc->core_class.superclass;
346 
347   if( sc->slider_class.draw_border == XtInheritDrawBorder )
348     sc->slider_class.draw_border = super->slider_class.draw_border ;
349 
350   if( sc->slider_class.draw_background == XtInheritDrawBackground )
351     sc->slider_class.draw_background = super->slider_class.draw_background ;
352 
353   if( sc->slider_class.draw_thumb == XtInheritDrawThumb )
354     sc->slider_class.draw_thumb = super->slider_class.draw_thumb ;
355 
356   if( sc->slider_class.move_thumb == XtInheritMoveThumb )
357     sc->slider_class.move_thumb = super->slider_class.move_thumb ;
358 
359   if( sc->slider_class.focus_proc == XtInheritFocusProc )
360     sc->slider_class.focus_proc = super->slider_class.focus_proc ;
361 
362   if( sc->slider_class.preferred_size == XtInheritPreferredSize )
363     sc->slider_class.preferred_size = super->slider_class.preferred_size ;
364 }
365 
366 
367 
368 	/* Init a newly created slider widget.  */
369 
370 
371 static void
SliderInit(Widget request,Widget new,ArgList args,Cardinal * num_args)372 SliderInit(Widget request, Widget new, ArgList args, Cardinal *num_args)
373 {
374     MwSliderWidget sw = (MwSliderWidget)new;
375     int	minimum = sw->slider.minimum ;
376     int	maximum = sw->slider.maximum ;
377 
378     /* defer GC allocation, etc., until Realize() time. */
379     sw->slider.foregroundGC =
380     sw->slider.backgroundGC =
381     sw->slider.greyGC =
382     sw->slider.topGC =
383     sw->slider.botGC = None ;
384     sw->slider.grey50 = None ;
385     if( maximum != minimum )
386       sw->slider.thumbpos =
387       	sw->slider.length*(sw->slider.value-minimum)/(maximum-minimum) ;
388     else
389       sw->slider.thumbpos = 0 ;
390 
391     if( sw->slider.step < 0 )
392       sw->slider.step = (sw->slider.maximum - sw->slider.minimum)/10 ;
393 
394     sw->slider.adjusting = False ;
395 
396 
397     /* if size not explicitly set, set it to our preferred size now. */
398 
399     if( request->core.width == 0 || request->core.height == 0 )
400     {
401       Dimension	w,h, dummy ;
402       SliderClass(sw)->slider_class.preferred_size(sw, &w,&h, &dummy,&dummy);
403       if( request->core.width == 0 ) new->core.width = w ;
404       if( request->core.height == 0 ) new->core.height = h ;
405       XtClass(new)->core_class.resize(new) ;
406     }
407 
408     sw->slider.needs_layout = True ;
409 }
410 
411 
412 
413 
414 
415 	/* Called when slider widget first realized.  Create the window
416 	 * and allocate the GCs
417 	 */
418 
419 static	void
SliderRealize(Widget w,Mask * valueMask,XSetWindowAttributes * attributes)420 SliderRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
421 {
422 	MwSliderWidget sw = (MwSliderWidget) w;
423 
424 	/* chain upwards */
425 	SuperClass->core_class.realize(w, valueMask, attributes);
426 
427 	SliderAllocGCs(sw) ;
428 }
429 
430 
431 
432 static	void
SliderDestroy(Widget w)433 SliderDestroy(Widget w)
434 {
435 	SliderFreeGCs((MwSliderWidget)w) ;
436 }
437 
438 
439 	/* Parent has resized us.  This will require that the slider be
440 	 * laid out again.
441 	 */
442 
443 static void
SliderResize(Widget w)444 SliderResize(Widget w)
445 {
446 	MwSliderWidget	sw = (MwSliderWidget) w;
447 	int		length ;
448 	int		s = sw->slider.shadowWidth ;
449 	int		minimum, maximum ;
450 
451 	/* compute working length of slider */
452 
453 	sw->slider.start = sw->slider.shadowWidth ;
454 
455 	if( sw->slider.orientation == XtorientHorizontal ) {
456 	  length = sw->core.width ;
457 	  sw->slider.thumbWidth = sw->core.height - s*2 ;
458 	}
459 	else {
460 	  length = sw->core.height ;
461 	  sw->slider.thumbWidth = sw->core.width - s*2 ;
462 	}
463 
464 	length -= + sw->slider.thumbLength + s*2 ;
465 	if( length < 0 ) length = 0 ;
466 	sw->slider.length = length ;
467 
468 	/* TODO: shrink thumb if it will make things fit? */
469 
470 	/* recompute thumb position */
471 	minimum = sw->slider.minimum ;
472 	maximum = sw->slider.maximum ;
473 	if( maximum != minimum )
474 	  sw->slider.thumbpos =
475 	    length*(sw->slider.value-minimum)/(maximum-minimum) ;
476 	else
477 	  sw->slider.thumbpos = 0 ;
478 
479 	if( sw->slider.autoScale )
480 	  sw->slider.inputScale = length < 100 ? length : 100 ;
481 
482 	sw->slider.needs_layout = False ;
483 }
484 
485 
486 
487 	/* Redraw entire Slider widget */
488 
489 static	void
SliderExpose(Widget w,XEvent * event,Region region)490 SliderExpose(Widget w, XEvent *event, Region region)
491 {
492 	MwSliderWidget	sw = (MwSliderWidget) w;
493 
494 	if( sw->slider.needs_layout )
495 	  XtClass(w)->core_class.resize(w) ;
496 
497 	SliderClass(sw)->slider_class.draw_border(sw) ;
498 	SliderClass(sw)->slider_class.draw_background(sw,
499 		0,0, w->core.width, w->core.height) ;
500 	SliderClass(sw)->slider_class.draw_thumb(sw) ;
501 }
502 
503 
504 	/* Called when any Slider widget resources are changed. */
505 
506 static	Boolean
SliderSetValues(Widget old,Widget request,Widget new,ArgList args,Cardinal * num_args)507 SliderSetValues(Widget old, Widget request, Widget new,
508 	ArgList args, Cardinal *num_args)
509 {
510 	MwSliderWidget oldsw = (MwSliderWidget) old ;
511 	MwSliderWidget sw = (MwSliderWidget) new ;
512 	Boolean	needRedraw = False ;
513 
514 	if( sw->slider.value != oldsw->slider.value  ||
515 	    sw->slider.minimum != oldsw->slider.minimum  ||
516 	    sw->slider.maximum != oldsw->slider.maximum )
517 	{
518 	  int	minimum = sw->slider.minimum ;
519 	  int	maximum = sw->slider.maximum ;
520 
521 	  if( maximum != minimum )
522 	    sw->slider.thumbpos =
523 	    	sw->slider.length*(sw->slider.value-minimum)/(maximum-minimum) ;
524 	  else
525 	    sw->slider.thumbpos = 0 ;
526 
527 	  needRedraw = True ;
528 	}
529 
530 	if( sw->slider.orientation != oldsw->slider.orientation  ||
531 	    sw->slider.thumbLength != oldsw->slider.thumbLength  ||
532 	    sw->slider.shadowWidth != oldsw->slider.shadowWidth ||
533 	    sw->slider.font != oldsw->slider.font )
534 	{
535 	  /* do some resizing stuff? */
536 	  needRedraw = True ;
537 	  sw->slider.needs_layout = True ;
538 	}
539 
540 	/* if any color changes, need to recompute GCs and redraw */
541 
542 	if( sw->core.background_pixel != oldsw->core.background_pixel ||
543 	    sw->core.background_pixmap != oldsw->core.background_pixmap ||
544 	    sw->slider.top_contrast != oldsw->slider.top_contrast  ||
545 	    sw->slider.bot_contrast != oldsw->slider.bot_contrast  ||
546 	    sw->slider.arm_contrast != oldsw->slider.arm_contrast  ||
547 	    sw->slider.insensitive_contrast !=
548 	    			oldsw->slider.insensitive_contrast  ||
549 	    sw->slider.be_nice_to_cmap != oldsw->slider.be_nice_to_cmap )
550 	{
551 	  /* brute-force method, recompute everything.  It would be
552 	   * more efficient to only recompute the GC's that are
553 	   * actually affected.
554 	   */
555 	  SliderFreeGCs(sw) ;
556 	  SliderAllocGCs(sw) ;
557 	  needRedraw = True ;
558 	}
559 
560 	if( sw->core.sensitive != oldsw->core.sensitive )
561 	  needRedraw = True ;
562 
563 	return needRedraw ;
564 }
565 
566 
567 
568 static	Boolean
SliderAcceptFocus(Widget w,Time * tm)569 SliderAcceptFocus( Widget w, Time *tm )
570 {
571     if( !w->core.being_destroyed  && XtIsRealized(w) &&
572 	XtIsSensitive(w) && w->core.visible && XtIsManaged(w) )
573     {
574       Widget p ;
575       for(p = XtParent(w); !XtIsShell(p); p = XtParent(p)) ;
576       XtSetKeyboardFocus(p, w) ;
577       return True ;
578     }
579     else
580       return False ;
581 }
582 
583 
584 
585 
586 
587 /*
588  * Return preferred size.  Accept changes to our preferred size,
589  * reject changes that are too small, return "almost" for sizes that
590  * are too big.
591  *
592  * These return values are highly subjective.  For example, many
593  * widgets (e.g. scrolled windows or text windows) would be perfectly
594  * happy with any size greater than the minimum.
595  */
596 
597 static XtGeometryResult
SliderQueryGeometry(Widget w,XtWidgetGeometry * intended,XtWidgetGeometry * preferred)598 SliderQueryGeometry(Widget w,
599 	XtWidgetGeometry *intended, XtWidgetGeometry *preferred)
600 {
601     register MwSliderWidget sw = (MwSliderWidget)w ;
602     XtGeometryMask mode = intended->request_mode ;
603     int	iw = intended->width ;
604     int	ih = intended->height ;
605     Dimension min_wid, min_hgt ;
606 
607     /* provide preferred size to caller, don't care about position,
608      * border_width, etc.
609      */
610     preferred->request_mode = CWWidth | CWHeight ;
611     SliderClass(sw)->slider_class.preferred_size (sw,
612     	&preferred->width, &preferred->height, &min_wid, &min_hgt) ;
613 
614     /* reject if caller proposing no change */
615     /* TODO: is this really worth it? */
616     if( (!(mode & CWX) || intended->x == w->core.x)  &&
617 	(!(mode & CWY) || intended->y == w->core.y)  &&
618 	(!(mode & CWBorderWidth) ||
619 		intended->border_width == w->core.border_width)  &&
620 	(!(mode & CWWidth) || iw == w->core.width)  &&
621 	(!(mode & CWHeight) || ih == w->core.height) )
622       return XtGeometryNo ;
623 
624     /* reject if too small */
625     if( ((mode & CWWidth) && iw < min_wid )  ||
626 	((mode & CWHeight) && ih < min_hgt ) )
627       return XtGeometryNo ;
628 
629     /* Since we don't mind being too big, accept anything else. */
630     return XtGeometryYes ;
631 }
632 
633 
634 
635 
636 
637 /****************************************************************
638  *
639  * Action Procedures
640  *
641  ****************************************************************/
642 
643 /*
644  * Rules for converting between pointer motion and slider motion:
645  *
646  *   Definitions:
647  *         spos = slider position
648  *         ds = change in slider position
649  *         length = domain of slider motion
650  *         range = maximum-minimum
651  *         dx = pointer motion
652  *         dv = change in value
653  *
654  *
655  *   Relationship between slider position and slider value are described
656  *   by this equation:
657  *
658  *         spos/length = (value-minimum)/(maximum-minimum)
659  *
660  *     convert value => slider position:
661  *
662  *         spos = length*(value-minimum)/(maximum-minimum)
663  *
664  *     convert slider position => value:
665  *
666  *         value = (maximum-minimum)*spos/length + minimum
667  *
668  *
669  *
670  *   Changes in pointer position are related to changes in slider
671  *   position:
672  *
673  *         ds = dx*inputScale/100
674  *
675  *
676  *   It's better to take pointer motion and map it directly to value
677  *   changes, especially with small sliders.
678  *
679  *         dv = ds*range/length
680  *         dv = dx*inputScale/100*range/length
681  *
682  *   There are three special cases that are worth optimizing:
683  *
684  *	inputScale = 100:
685  *
686  *         dv = dx*range/length
687  *
688  *	inputScale = length:
689  *	length = 0:
690  *
691  *         dv = dx*range/100
692  *
693  */
694 
695 
696 
697 
698 
699 static	void
StartAdjust(Widget w,XEvent * ev,String * args,Cardinal * num_args)700 StartAdjust(Widget w, XEvent *ev, String *args, Cardinal *num_args)
701 {
702 	MwSliderWidget sw = (MwSliderWidget)w ;
703 	int	s = sw->slider.shadowWidth ;
704 	int	pt ;
705 
706 	/* find out if this was a click on the thumb, or the
707 	 * background.
708 	 */
709 	if( sw->slider.orientation == XtorientHorizontal )
710 	  pt = s + ev->xbutton.x ;
711 	else
712 	  pt = sw->core.height - s - ev->xbutton.y ;
713 
714 	if( pt < sw->slider.thumbpos + 2*s )
715 	  ChangeSliderValue(sw, sw->slider.value - sw->slider.step) ;
716 	else if( pt > sw->slider.thumbpos + 2*s + sw->slider.thumbLength )
717 	  ChangeSliderValue(sw, sw->slider.value + sw->slider.step) ;
718 	else {
719 	  sw->slider.oldvalue = sw->slider.value ;
720 	  sw->slider.adjustPt = pt ;
721 	  sw->slider.adjusting = True ;
722 	}
723 }
724 
725 
726 static	void
HandleThumb(Widget w,XEvent * ev,String * args,Cardinal * num_args)727 HandleThumb(Widget w, XEvent *ev, String *args, Cardinal *num_args)
728 {
729 	MwSliderWidget sw = (MwSliderWidget)w ;
730 	int	s = sw->slider.shadowWidth ;
731 	int	pt, dp, dv ;
732 	int	length = sw->slider.length ;
733 	int	range = sw->slider.maximum - sw->slider.minimum ;
734 
735 	if( !sw->slider.adjusting ) return ;
736 
737 	if( length <= 0 ) length = 1 ;
738 
739 	if( sw->slider.orientation == XtorientHorizontal )
740 	  pt = s + ev->xbutton.x ;
741 	else
742 	  pt = sw->core.height - s - ev->xbutton.y ;
743 
744 	dp = pt - sw->slider.adjustPt ;
745 
746 	/* special cases */
747 	if( sw->slider.inputScale == 100 )
748 	  dv = dp*range/length ;
749 	else if( sw->slider.inputScale == length )
750 	  dv = dp*range/100 ;
751 	else
752 	  dv = dp*sw->slider.inputScale*range/length/100 ;
753 
754 	ChangeSliderValue(sw, sw->slider.oldvalue + dv) ;
755 	XtCallCallbackList(w,
756 		sw->slider.dragCallback, (XtPointer)sw->slider.value) ;
757 }
758 
759 
760 static	void
EndAdjust(Widget w,XEvent * ev,String * args,Cardinal * num_args)761 EndAdjust(Widget w, XEvent *ev, String *args, Cardinal *num_args)
762 {
763 	MwSliderWidget sw = (MwSliderWidget)w ;
764 
765 	if( !sw->slider.adjusting ) return ;
766 
767 	sw->slider.adjusting = False ;
768 }
769 
770 
771 static	void
Adjust(Widget w,XEvent * ev,String * args,Cardinal * num_args)772 Adjust(Widget w, XEvent *ev, String *args, Cardinal *num_args)
773 {
774 	MwSliderWidget sw = (MwSliderWidget)w ;
775 	int newvalue = sw->slider.value ;
776 
777 	if( *num_args == 0 ) return ;	/* TODO: warning? */
778 
779 	if( *num_args >= 1 )
780 	{
781 	  Bool neg = False ;
782 	  int n = 0 ;
783 	  String str = *args ;
784 	  if( *str == '-' ) { neg = True ; ++str ; }
785 	  if( isdigit(*str) )
786 	    n = atoi(str) ;
787 	  else if( XmuCompareISOLatin1(str, "step2") == 0 )
788 	    n = sw->slider.step2 ;
789 	  else if( XmuCompareISOLatin1(str, "step") == 0 )
790 	    n = sw->slider.step ;
791 	  else if( XmuCompareISOLatin1(str, "home") == 0 )
792 	    newvalue = sw->slider.minimum ;
793 	  else if( XmuCompareISOLatin1(str, "end") == 0 )
794 	    newvalue = sw->slider.maximum ;
795 
796 	  if( neg ) n = -n ;
797 	  newvalue += n ;
798 
799 	  ChangeSliderValue(sw, newvalue) ;
800 	}
801 }
802 
803 static	void
Notify(Widget w,XEvent * ev,String * args,Cardinal * num_args)804 Notify(Widget w, XEvent *ev, String *args, Cardinal *num_args)
805 {
806 	MwSliderWidget sw = (MwSliderWidget)w ;
807 	XtCallCallbackList(w,
808 		sw->slider.valueChangedCallback, (XtPointer)sw->slider.value) ;
809 }
810 
811 
812 static	void
Focus(Widget w,XEvent * ev,String * args,Cardinal * num_args)813 Focus(Widget w, XEvent *ev, String *args, Cardinal *num_args)
814 {
815 	MwSliderWidget sw = (MwSliderWidget)w ;
816 
817 	if( *num_args == 0 ) return ;	/* TODO: warning? */
818 
819 	sw->slider.has_focus = atoi(*args) ;
820 
821 	SliderClass(sw)->slider_class.focus_proc(sw) ;
822 }
823 
824 
825 
826 
827 
828 
829 /****************************************************************
830  *
831  * Public Procedures
832  *
833  ****************************************************************/
834 
835 
836 	/* Set the top fc, optionally call all callbacks. */
837 void
XawSliderSetValue(Widget w,int newvalue,Bool callCallbacks)838 XawSliderSetValue(Widget w, int newvalue, Bool callCallbacks)
839 {
840 	MwSliderWidget	sw = (MwSliderWidget) w;
841 	if( !XtIsSubclass(w, mwSliderWidgetClass) ) return ;
842 	ChangeSliderValue(sw, newvalue) ;
843 	if( callCallbacks)
844 	  XtCallCallbackList(w,
845 		sw->slider.valueChangedCallback, (XtPointer)sw->slider.value) ;
846 }
847 
848 
849 
850 
851 /****************************************************************
852  *
853  * Private Procedures
854  *
855  ****************************************************************/
856 
857 
858 /*
859  * Overview:
860  *
861  *   draw_border() draws the "static" part of the widget, that is, any
862  *   part that will never be obscured by the thumb.  In the base class,
863  *   this means the shadow border.  This function should also draw the
864  *   keyboard focus feedback, possibly by calling focus_proc().
865  *
866  *   draw_background() draws whatever part of the slider is under the
867  *   thumb.  It is used during expose() and to repair damage made by
868  *   moving the thumb.  It accepts as arguments a region to be
869  *   redrawn.  This may describe a region just vacated by the thumb,
870  *   or it may describe the entire widget.  This function may also need
871  *   to draw the keyboard focus feedback, if such feedback can be damaged
872  *   by thumb motion.  This function will not be called with width or
873  *   height equal to zero.
874  *
875  *   draw_thumb() draws the thumb at the current location.
876  *
877  *   move_thumb() moves the thumb to a new location.  In the base class,
878  *   this function uses XCopyArea() to move the thumb, and draw_background()
879  *   to repair the damage.  XCopyArea() may not be suitable if the thumb
880  *   is non-rectangular.
881  *
882  *   focus_proc() is called in response to changes in keyboard focus;
883  *   it draws the visual indication that the slider has gained
884  *   or has lost keyboard focus.  This proc examines the has_focus
885  *   variable.
886  *
887  *   preferred_size() returns the slider's preferred size, and the
888  *   minimum acceptable size.
889  *
890  *   Variables:
891  *
892  *     start       the first pixel the thumb may ever occupy
893  *
894  *     length      the working length of the slider -- i.e. the total
895  *                 adjusting range of the thumb.  The first pixel
896  *                 occupied by the thumb may range from start to
897  *                 start+length.
898  *
899  *     thumbpos    current location of the thumb; ranges from 0 to length
900  *
901  *     thumbLength length of the thumb.  The last pixel occupied by the
902  *                 thumb may range from (start + thumbLength - 1) to
903  *                 (start + length + thumbLength -1)
904  *
905  *     thumbWidth  width of the thumb; used by move_thumb() to determine
906  *		   the size of the region to be copied.  Note that for
907  *		   horizontal sliders, this is the vertical dimension.
908  *
909  */
910 
911 
912 
913 static	void
SliderDrawBorder(MwSliderWidget sw)914 SliderDrawBorder(MwSliderWidget sw)
915 {
916 	Draw3dBox((Widget)sw, 0,0, sw->core.width, sw->core.height,
917 		sw->slider.shadowWidth, sw->slider.botGC, sw->slider.topGC) ;
918 }
919 
920 
921 static	void
SliderDrawBackground(MwSliderWidget sw,int x,int y,int wid,int hgt)922 SliderDrawBackground(MwSliderWidget sw, int x, int y, int wid, int hgt)
923 {
924 	Widget	w = (Widget)sw ;
925 	int	s = sw->slider.shadowWidth ;
926 	int	x2 = x+wid ;
927 	int	y2 = y+hgt ;
928 	int	x3 = s ;
929 	int	y3 = s ;
930 	int	x4 = sw->core.width - s ;
931 	int	y4 = sw->core.height - s ;
932 
933 	/* easy; just clip the requested region to within the shadow
934 	 * border and fill.
935 	 */
936 
937 	if( x < x3 ) x = x3 ;
938 	if( y < y3 ) y = y3 ;
939 	if( x2 > x4 ) x2 = x4 ;
940 	if( y2 > y4 ) y2 = y4 ;
941 
942 	XFillRectangle(XtDisplay(w), XtWindow(w), sw->slider.greyGC,
943 	  x,y, x2-x, y2-y) ;
944 }
945 
946 
947 	/* Utility: return the bounds of the thumb */
948 
949 void
_SliderThumb(MwSliderWidget sw,int * x,int * y,int * wid,int * hgt)950 _SliderThumb(MwSliderWidget sw, int *x, int *y, int *wid, int *hgt)
951 {
952 	int	st = sw->slider.start + sw->slider.thumbpos ;
953 	int	tl = sw->slider.thumbLength ;
954 	int	tw = sw->slider.thumbWidth ;
955 
956 	if( sw->slider.thumbpos < 0 )
957 	  sw->slider.thumbpos = 0 ;
958 	else if( sw->slider.thumbpos > sw->slider.length )
959 	  sw->slider.thumbpos = sw->slider.length ;
960 
961 	/* assume thumb centered in slider */
962 
963 	if( sw->slider.orientation == XtorientHorizontal ) {
964 	  *wid = tl ;
965 	  *hgt = tw ;
966 	  *x = st ;
967 	  *y = (sw->core.height - tw)/2 ;
968 	}
969 	else {
970 	  *wid = tw ;
971 	  *hgt = tl ;
972 	  *x = (sw->core.width - tw)/2 ;
973 	  *y = sw->core.height - st - tl ;
974 	}
975 }
976 
977 
978 static	void
SliderDrawThumb(MwSliderWidget sw)979 SliderDrawThumb(MwSliderWidget sw)
980 {
981 	Display	*dpy = XtDisplay((Widget)sw) ;
982 	Window	win = XtWindow((Widget)sw) ;
983 	int	s = sw->slider.shadowWidth ;
984 	int	x,y, wid,hgt ;
985 	GC	botgc = sw->slider.botGC ;
986 	GC	topgc = sw->slider.topGC ;
987 
988 	_SliderThumb(sw, &x, &y, &wid, &hgt) ;
989 
990 	XClearArea(dpy,win, x,y, wid,hgt, False) ;
991 
992 	if( sw->slider.orientation == XtorientHorizontal ) {
993 	  XDrawLine(dpy,win, botgc, x+wid/2-1, y, x+wid/2-1, y+hgt-1) ;
994 	  XDrawLine(dpy,win, topgc, x+wid/2, y, x+wid/2, y+hgt-1) ;
995 	}
996 	else {
997 	  XDrawLine(dpy,win, botgc, x, y+hgt/2-1, x+wid-1, y+hgt/2-1) ;
998 	  XDrawLine(dpy,win, topgc, x, y+hgt/2, x+wid-1, y+hgt/2) ;
999 	}
1000 	Draw3dBox((Widget)sw, x,y, wid, hgt, s, topgc, botgc) ;
1001 }
1002 
1003 
1004 static	void
SliderMoveThumb(MwSliderWidget sw,int newpos)1005 SliderMoveThumb(MwSliderWidget sw, int newpos)
1006 {
1007 	Display	*dpy = XtDisplay((Widget)sw) ;
1008 	Window	win = XtWindow((Widget)sw) ;
1009 	int	sx,sy, dx,dy, wid,hgt ;
1010 	int	oldpos = sw->slider.thumbpos ;
1011 	GC	gc = sw->slider.backgroundGC ;
1012 
1013 	/* assume the thumb is centered in the slider */
1014 
1015 	if( newpos < 0 )
1016 	  newpos = 0 ;
1017 	else if( newpos > sw->slider.length )
1018 	  newpos = sw->slider.length ;
1019 
1020 	if( newpos == oldpos )
1021 	  return ;
1022 
1023 	_SliderThumb(sw, &sx, &sy, &wid, &hgt) ;
1024 
1025 	dx = sx ;
1026 	dy = sy ;
1027 	if( sw->slider.orientation == XtorientHorizontal )
1028 	  dx += newpos - oldpos ;
1029 	else
1030 	  dy -= newpos - oldpos ;
1031 	XCopyArea(dpy,win,win, gc, sx,sy, wid,hgt, dx,dy) ;
1032 
1033 	if( sw->slider.orientation == XtorientHorizontal ) {
1034 	  /* fill in left or right? */
1035 	  if( newpos > oldpos ) {	/* right */
1036 	    dx = sx ;
1037 	    if( newpos - oldpos < wid ) wid = newpos - oldpos ;
1038 	  }
1039 	  else {			/* left */
1040 	    dx = sx + wid ;
1041 	    if( oldpos - newpos < wid ) wid = oldpos - newpos ;
1042 	    dx -= wid ;
1043 	  }
1044 	}
1045 	else {
1046 	  /* fill in top or bottom? */
1047 	  if( newpos > oldpos ) {	/* bottom */
1048 	    dy = sy + hgt ;
1049 	    if( newpos - oldpos < hgt ) hgt = newpos - oldpos ;
1050 	    dy -= hgt ;
1051 	  }
1052 	  else {			/* top */
1053 	    dy = sy ;
1054 	    if( oldpos - newpos < hgt ) hgt = oldpos - newpos ;
1055 	  }
1056 	}
1057 	SliderClass(sw)->slider_class.draw_background(sw, dx,dy, wid, hgt) ;
1058 
1059 	sw->slider.thumbpos = newpos ;
1060 }
1061 
1062 
1063 	/* In the base class, we indicate focus by a solid border that
1064 	 * will at least partially overlay the 3-d border.  Undraw the
1065 	 * solid border by simply redrawing the 3-d border.
1066 	 */
1067 
1068 static	void
SliderFocusProc(MwSliderWidget sw)1069 SliderFocusProc(MwSliderWidget sw)
1070 {
1071 	if( sw->slider.has_focus )
1072 	  XDrawRectangle(XtDisplay((Widget)sw), XtWindow((Widget)sw),
1073 	    sw->slider.foregroundGC,
1074 	    0,0, sw->core.width-1, sw->core.height-1) ;
1075 	else
1076 	  SliderDrawBorder(sw) ;
1077 }
1078 
1079 
1080 static	void
ChangeSliderValue(MwSliderWidget sw,int newvalue)1081 ChangeSliderValue(MwSliderWidget sw, int newvalue)
1082 {
1083 	int	newpos ;
1084 	int	minimum = sw->slider.minimum ;
1085 	int	maximum = sw->slider.maximum ;
1086 
1087 	if( newvalue < minimum )
1088 	  newvalue = minimum ;
1089 	else if( newvalue > maximum )
1090 	  newvalue = maximum ;
1091 
1092 	if( maximum != minimum )
1093 	  newpos = sw->slider.length*(newvalue-minimum)/(maximum-minimum) ;
1094 	else
1095 	  newpos = 0 ;
1096 	SliderClass(sw)->slider_class.move_thumb(sw, newpos) ;
1097 	sw->slider.value = newvalue ;
1098 }
1099 
1100 
1101 
1102 static	void
SliderAllocGCs(MwSliderWidget sw)1103 SliderAllocGCs(MwSliderWidget sw)
1104 {
1105 	Widget w = (Widget) sw;
1106 
1107 	if( !XtIsRealized(w) )
1108 	  return ;
1109 
1110 	sw->slider.backgroundGC = AllocBackgroundGC((Widget)sw, None) ;
1111 	sw->slider.foregroundGC = AllocFgGC(w,
1112 		sw->slider.foreground, sw->slider.font->fid) ;
1113 	sw->slider.topGC = AllocTopShadowGC(w,
1114 		sw->slider.top_contrast, sw->slider.be_nice_to_cmap) ;
1115 	sw->slider.botGC = AllocBotShadowGC(w,
1116 		sw->slider.bot_contrast, sw->slider.be_nice_to_cmap) ;
1117 	sw->slider.greyGC = AllocArmGC(w,
1118 		sw->slider.arm_contrast, sw->slider.be_nice_to_cmap) ;
1119 }
1120 
1121 
1122 static	void
SliderFreeGCs(MwSliderWidget sw)1123 SliderFreeGCs(MwSliderWidget sw)
1124 {
1125 	Widget w = (Widget) sw;
1126 
1127 	if( !XtIsRealized(w) )
1128 	  return ;
1129 
1130 	XtReleaseGC(w, sw->slider.backgroundGC) ;
1131 	XtReleaseGC(w, sw->slider.foregroundGC) ;
1132 	XtReleaseGC(w, sw->slider.topGC) ;
1133 	XtReleaseGC(w, sw->slider.botGC) ;
1134 	XtReleaseGC(w, sw->slider.greyGC) ;
1135 	XmuReleaseStippledPixmap(XtScreen(w), sw->slider.grey50) ;
1136 }
1137 
1138 
1139 
1140 
1141 
1142 
1143 
1144 
1145 
1146 
1147 /****************************************************************
1148  *
1149  * Geometry Utilities
1150  *
1151  ****************************************************************/
1152 
1153 
1154 
1155 
1156 	/*
1157 	 * Find preferred size.  Depends on widget contents.
1158 	 */
1159 
1160 
1161 static	void
SliderPreferredSize(MwSliderWidget sw,Dimension * reply_width,Dimension * reply_height,Dimension * min_wid,Dimension * min_hgt)1162 SliderPreferredSize(MwSliderWidget sw,
1163 	Dimension *reply_width, Dimension *reply_height,
1164 	Dimension *min_wid, Dimension *min_hgt)
1165 {
1166 	int			s = sw->slider.shadowWidth ;
1167 
1168 	if( sw->slider.orientation == XtorientHorizontal ) {
1169 	  *reply_width = DEF_WID ;
1170 	  *reply_height = THUMB_HGT + s*4 ;
1171 	  *min_wid = sw->slider.thumbLength + s*2 + 1 ;
1172 	  *min_hgt = s*2 + 3 ;
1173 	}
1174 	else {
1175 	  *reply_width = THUMB_HGT + s*4 ;
1176 	  *reply_height = DEF_WID ;
1177 	  *min_wid = s*2 + 3 ;
1178 	  *min_hgt = sw->slider.thumbLength + s*2 + 1 ;
1179 	}
1180 }
1181