1 /* $Id: Ruler.c,v 1.2 1999/08/31 19:44:36 falk Exp $
2  *
3  * Ruler.c - Ruler widget
4  *
5  * Author: Edward A. Falk
6  *	   falk@falconer.vip.best.com
7  *
8  * Date: July, 1999
9  *
10  * $Log: Ruler.c,v $
11  * Revision 1.2  1999/08/31 19:44:36  falk
12  * fixed bug during initial sizing
13  * no longer uses regions
14  * now uses GraphicsExposure
15  *
16  * Revision 1.1  1999/08/27 18:33:57  falk
17  * Initial revision
18  *
19  *
20  */
21 
22 /* TODO:
23  *  RulerSetMin(rw, -5.) screws up.
24  *  Graphics exposure?
25  */
26 
27 /* General notes:
28  *
29  * The ruler dimensions, etc. are represented as floating-point
30  * numbers.  These must be converted to integers to pass to Xlib.
31  *
32  * The obvious way to do this is to compute
33  *
34  *	x = x0 + (v - vmin) * scale
35  *
36  * where x0 is the pixel coordinate of the left edge of the ruler,
37  * vmin is the ruler value at x0, and scale is the number of pixels
38  * per ruler unit.
39  *
40  * Unfortunately, this formula contains the potential for round-off
41  * error.  When vmin changes, there is a chance for the x value
42  * to be off by one pixel.  In practice, this means that if you
43  * slide the ruler back and forth long enough, eventually you will
44  * see a label or tic mark drawn double because of a one-pixel offset.
45  *
46  * Here is a concrete example:
47  *	vmin = -1.5
48  *	scale = 100
49  *	x0 = 0
50  *	find: label for v=-1.:
51  *
52  *	x = (-1 - -1.5) * 100 = 50
53  *
54  *	scroll right 1 pixel:
55  *	new vmin = -1.5099999
56  *	x = (-1 - -1.50999) * 100 = 50	<= round off error!
57  *
58  *
59  * One solution is to compute:
60  *
61  *	x = l0 + v * scale
62  *
63  * where l0 is the computed pixel location of ruler value 0.:
64  *
65  *	l0 = x0 + (0. - vmin) * scale
66  *
67  * As long as l0 is not recomputed, or is recomputed using only
68  * integer arithmatic, or a complete redraw is executed after
69  * computing l0, then round-off error will no longer be a problem.
70  *
71  * One problem remains:  The domain of float numbers is much larger
72  * than for integers.  It is possible that for very large magnitude
73  * values of vmin, l0 may overflow.  We can reduce, but not solve the
74  * problem by representing l0 with the largest integers available.
75  *
76  * A better solution is for l0 to represent not the pixel value of 0.,
77  * but of some other value, such as an arbitrary point within a million
78  * pixels of the ruler.  This would require recomputing l0 every time
79  * its magnitude became too great.  The formula would now look like:
80  *
81  *	x = l0 + (v - v0) * scale
82  *
83  * where v0 is the ruler value which lies at l0.  If we use vmin for the
84  * initial value of v0, then the initial value of l0 is simply x0.
85  *
86  * As long as changes to l0 and v0 are made with integer math, or a
87  * complete redraw is executed after recomputing l0 and v0, there
88  * should be no problem with round-off error.
89  */
90 
91 #include	<stdio.h>
92 #include	<ctype.h>
93 #include	<string.h>
94 #include	<stdlib.h>
95 #include	<limits.h>
96 
97 #include	<X11/cursorfont.h>
98 #include	<X11/IntrinsicP.h>
99 #include	<X11/StringDefs.h>
100 #include	<X11/Xmu/Misc.h>
101 #include	<X11/Xmu/CharSet.h>
102 #include	<X11/Xaw/XawInit.h>
103 #include	"MwRulerP.h"
104 #include	"MwGcs.h"
105 
106 #define	RULER_MIN	4	/* min space between tic marks */
107 #define	POINTER_SIZE	7
108 #define	PS2		(POINTER_SIZE*2+1)
109 #define	TIC_LEN		12	/* longest tic mark */
110 #define	IMARGIN		6	/* internal margin between tics & text */
111 
112 #define	MAX_L0		(LONG_MAX/1024)	/* arbitrary, really */
113 
114 
115 
116 /****************************************************************
117  *
118  * Ruler Resources
119  *
120  ****************************************************************/
121 
122 static	char	defaultTranslations[] =
123 	"<BtnDown>:	StartScroll()\n\
124 	 <BtnMotion>:	Drag()\n\
125 	 <BtnUp>:	EndScroll()\n\
126 	 <KeyDown>Page_Up:	Scroll(-page)\n\
127 	 <KeyDown>Page_Down:	Scroll(page)\n\
128 	 Shift<KeyDown>Left:	Scroll(-page)\n\
129 	 Shift<KeyDown>KP_Left:	Scroll(-page)\n\
130 	 Shift<KeyDown>Up:	Scroll(-page)\n\
131 	 Shift<KeyDown>KP_Up:	Scroll(-page)\n\
132 	 Shift<KeyDown>Right:	Scroll(page)\n\
133 	 Shift<KeyDown>KP_Right: Scroll(page)\n\
134 	 Shift<KeyDown>Down:	Scroll(page)\n\
135 	 Shift<KeyDown>KP_Down:	Scroll(page)\n\
136 	 Ctrl<KeyDown>Left:	Scroll(-1)\n\
137 	 Ctrl<KeyDown>KP_Left:	Scroll(-1)\n\
138 	 Ctrl<KeyDown>Up:	Scroll(-1)\n\
139 	 Ctrl<KeyDown>KP_Up:	Scroll(-1)\n\
140 	 Ctrl<KeyDown>Right:	Scroll(1)\n\
141 	 Ctrl<KeyDown>KP_Right:	Scroll(1)\n\
142 	 Ctrl<KeyDown>Down:	Scroll(1)\n\
143 	 Ctrl<KeyDown>KP_Down:	Scroll(1)\n\
144 	 <KeyDown>Left:		Scroll(-tic)\n\
145 	 <KeyDown>KP_Left:	Scroll(-tic)\n\
146 	 <KeyDown>Up:		Scroll(-tic)\n\
147 	 <KeyDown>KP_Up:	Scroll(-tic)\n\
148 	 <KeyDown>Right:	Scroll(tic)\n\
149 	 <KeyDown>KP_Right:	Scroll(tic)\n\
150 	 <KeyDown>Down:		Scroll(tic)\n\
151 	 <KeyDown>KP_Down:	Scroll(tic)\n\
152 ";
153 
154 
155 static	float	float0 = 0.0 ;
156 static	float	float100 = 100.0 ;
157 
158 #define	offset(field)	XtOffsetOf(MwRulerRec, ruler.field)
159 static XtResource resources[] = {
160 
161   {XtNmarkStep, XtCStep, XtRInt, sizeof(int),
162       offset(markStep), XtRImmediate, (XtPointer) 1},
163   {XtNmarkDiv, XtCDivisions, XtRInt, sizeof(int),
164       offset(markDiv), XtRImmediate, (XtPointer) 32},
165   {XtNlabelStep, XtCStep, XtRInt, sizeof(int),
166       offset(labelStep), XtRImmediate, (XtPointer) 1},
167   {XtNlabelDiv, XtCDivisions, XtRInt, sizeof(int),
168       offset(labelDiv), XtRImmediate, (XtPointer) 32},
169   {XtNlabelStyle, XtCLabelStyle, XtRLabelStyle, sizeof(XtLabelStyle),
170       offset(labelStyle), XtRImmediate, (XtPointer) DECIMAL},
171   {XtNorientation, XtCOrientation, XtRGravity, sizeof(int),
172       offset(orientation), XtRImmediate, (XtPointer) NorthGravity},
173   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
174        offset(foreground), XtRString, XtDefaultForeground},
175   {XtNpointerColor, XtCForeground, XtRPixel, sizeof(Pixel),
176        offset(pointerColor), XtRString, XtDefaultForeground},
177   {XtNshowPointer, XtCShowPointer, XtRBoolean, sizeof(Boolean),
178        offset(showPointer), XtRImmediate, (XtPointer)False},
179   {XtNminValue, XtCMinValue, XtRFloat, sizeof(float),
180        offset(minValue), XtRFloat, (XtPointer)&float0},
181   {XtNvalue, XtCValue, XtRFloat, sizeof(float),
182        offset(value), XtRFloat, (XtPointer)&float0},
183   {XtNscale, XtCScale, XtRFloat, sizeof(float),
184        offset(scale), XtRFloat, (XtPointer)&float100},
185   {XtNiValue, XtCIValue, XtRInt, sizeof(int),
186       offset(iValue), XtRImmediate, (XtPointer) 0},
187   { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
188 	offset(font), XtRString, XtDefaultFont},
189   { XtNfracFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
190 	offset(fracFont), XtRString, NULL},
191   { XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
192 	offset(cursor), XtRImmediate, NULL},
193   { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
194          offset(callbacks), XtRCallback, NULL},
195   {XtNuserData, XtCUserData, XtRPointer, sizeof(XtPointer),
196       offset(userData), XtRImmediate, NULL},
197 };
198 #undef	offset
199 
200 
201 
202 #ifdef	__STDC__
203 
204 	/* member functions */
205 static	void	RulerClassInit() ;
206 static	void	RulerInit( Widget req, Widget new, ArgList, Cardinal *) ;
207 static	void	RulerRealize( Widget, Mask *, XSetWindowAttributes *) ;
208 static	void	RulerDestroy( Widget ) ;
209 static	void	RulerResize( Widget ) ;
210 static	void	RulerExpose( Widget, XEvent *event, Region region) ;
211 static	Boolean	RulerSetValues( Widget, Widget, Widget, ArgList, Cardinal *) ;
212 static	XtGeometryResult RulerQueryGeometry( Widget,
213 		    XtWidgetGeometry *intended, XtWidgetGeometry *preferred) ;
214 
215 	/* actions */
216 static	void	StartScroll( Widget, XEvent *, String *, Cardinal *) ;
217 static	void	Drag( Widget, XEvent *, String *, Cardinal *) ;
218 static	void	EndScroll( Widget, XEvent *, String *, Cardinal *) ;
219 static	void	Scroll( Widget, XEvent *, String *, Cardinal *) ;
220 static	void	ScrollTo( Widget, XEvent *, String *, Cardinal *) ;
221 
222 
223 	/* internal privates */
224 
225 static	void	PreferredSize( MwRulerWidget, Dimension *wid, Dimension *hgt) ;
226 static	void	RulerStepDiv(MwRulerWidget) ;
227 static	void	RulerDraw( MwRulerWidget, int x1, int x2) ;
228 static	void	drawTics( MwRulerWidget, int, int, int, int, int, int, int) ;
229 static	void	drawTic( MwRulerWidget, int x, int, int, int x1, int x2) ;
230 static	void	drawLabel( MwRulerWidget, int, int, String, GC, XFontStruct *) ;
231 static	void	indexPmPos(MwRulerWidget, Position *, Position *,
232 				Dimension *, Dimension *) ;
233 static	void	drawPointer( MwRulerWidget ) ;
234 static	void	undrawPointer( MwRulerWidget ) ;
235 static	void	iScroll( MwRulerWidget, int dp) ;
236 static	void	ExtractPosition ( XEvent *event, Position *x, Position *y) ;
237 static	void	RulerAllocGCs( MwRulerWidget ) ;
238 static	void	RulerFreeGCs( MwRulerWidget ) ;
239 static	void	RulerAllocFgGC( MwRulerWidget ) ;
240 static	void	RulerAllocPointerGC( MwRulerWidget ) ;
241 static	Boolean	CvtStringToLabelStyle( Display *, XrmValuePtr, Cardinal *,
242 			XrmValuePtr from, XrmValuePtr to, XtPointer *) ;
243 
244 static	int	ifloor( double a) ;
245 static	int	iceil( double a) ;
246 static	void	fracStr( MwRulerWidget, char line[], int v, int n, int d, int s) ;
247 static	int	fracWid(MwRulerWidget, int v, int divisions) ;
248 static	int	rulerWid(MwRulerWidget, int v, int divisions) ;
249 static	int	minStep( int step, int sm, double scale) ;
250 static	int	maxDiv( MwRulerWidget, int d, int wid, int (*func)(), int a) ;
251 
252 #else	/* K&R C */
253 
254 
255 	/* member functions */
256 
257 static	void	RulerClassInit();
258 static	void	RulerInit();
259 static	void	RulerResize();
260 static	void	RulerExpose();
261 static	void	RulerDestroy();
262 static	void	RulerRealize();
263 static	Boolean	RulerSetValues();
264 
265 	/* action procs */
266 
267 static	void	StartScroll(), Drag(), EndScroll() ;
268 static	void	Scroll() ;
269 static	void	ScrollTo() ;
270 
271 	/* internal privates */
272 
273 static	void	RulerStepDiv() ;
274 static	void	RulerDraw() ;
275 static	void	drawTics() ;
276 static	void	drawTic() ;
277 static	void	drawLabel() ;
278 static	void	indexPmPos() ;
279 static	void	drawPointer() ;
280 static	void	undrawPointer() ;
281 static	void	RulerAllocGCs() ;	/* get rendering GCs */
282 static	void	RulerFreeGCs() ;	/* return rendering GCs */
283 static	void	PreferredSize() ;	/* compute preferred size */
284 static	int	RulerLayout() ;		/* lay out ruler */
285 
286 static	void	RulerDraw3dBox() ;
287 
288 static	void	RulerAllocFgGC() ;
289 static	void	RulerAllocPointerGC() ;
290 static	void	assignTicHgt() ;
291 static	int	minStep() ;
292 static	int	fracWid() ;
293 static	int	rulerWid() ;
294 static	int	maxDiv() ;
295 static	void	iScroll() ;
296 static	void	ExtractPosition() ;
297 static	Boolean	CvtStringToLabelStyle() ;
298 static	int	ifloor() ;
299 static	int	iceil() ;
300 static	void	fracStr() ;
301 
302 #endif
303 
304 
305 #ifndef	max
306 #define	max(a,b) (((a) > (b)) ? (a) : (b))
307 #define	min(a,b) (((a) < (b)) ? (a) : (b))
308 #define	abs(a)	(((a) < 0) ? -(a) : (a))
309 #endif
310 
311 
312 #define	fontAscent(f)	((f != NULL) ? (f)->ascent : 0)
313 
314 
315 
316 static	XtActionsRec	actionsList[] =
317   {
318     {"StartScroll",	StartScroll},
319     {"Drag",		Drag},
320     {"EndScroll",	EndScroll},
321     {"Scroll",		Scroll},
322     {"ScrollTo",	ScrollTo},
323   } ;
324 
325 
326 /****************************************************************
327 *
328 * Full class record constant
329 *
330 ****************************************************************/
331 
332 #define	SuperClass	(&coreClassRec)
333 
334 MwRulerClassRec mwRulerClassRec = {
335   {
336 /* core_class fields      */
337     /* superclass         */    (WidgetClass) SuperClass,
338     /* class_name         */    "MwRuler",
339     /* widget_size        */    sizeof(MwRulerRec),
340     /* class_initialize   */    RulerClassInit,
341     /* class_part_init    */	NULL,			/* TODO? */
342     /* class_inited       */	FALSE,
343     /* initialize         */    RulerInit,
344     /* initialize_hook    */	NULL,
345     /* realize            */    RulerRealize,
346     /* actions            */    actionsList,
347     /* num_actions	  */	XtNumber(actionsList),
348     /* resources          */    resources,
349     /* num_resources      */    XtNumber(resources),
350     /* xrm_class          */    NULLQUARK,
351     /* compress_motion	  */	TRUE,
352     /* compress_exposure  */	XtExposeCompressSeries|XtExposeGraphicsExposeMerged|XtExposeNoRegion,
353     /* compress_enterleave*/	TRUE,
354     /* visible_interest   */    FALSE,
355     /* destroy            */    RulerDestroy,
356     /* resize             */    RulerResize,
357     /* expose             */    RulerExpose,
358     /* set_values         */    RulerSetValues,
359     /* set_values_hook    */	NULL,
360     /* set_values_almost  */    XtInheritSetValuesAlmost,
361     /* get_values_hook    */	NULL,
362     /* accept_focus       */    NULL,
363     /* version            */	XtVersion,
364     /* callback_private   */    NULL,
365     /* tm_table           */    defaultTranslations,
366     /* query_geometry     */	RulerQueryGeometry,
367     /* display_accelerator*/	XtInheritDisplayAccelerator,
368     /* extension          */	NULL
369   },
370   {
371 /* Ruler class fields */
372     /* extension	  */	NULL,
373   }
374 };
375 
376 WidgetClass mwRulerWidgetClass = (WidgetClass)&mwRulerClassRec;
377 
378 
379 #ifdef	DEBUG
380 #ifdef	__STDC__
381 #define	assert(e) \
382 	  if(!(e)) fprintf(stderr,"yak! %s at %s:%d\n",#e,__FILE__,__LINE__)
383 #else
384 #define	assert(e) \
385 	  if(!(e)) fprintf(stderr,"yak! e at %s:%d\n",__FILE__,__LINE__)
386 #endif
387 #else
388 #define	assert(e)
389 #endif
390 
391 
392 
393 
394 /****************************************************************
395  *
396  * Member Procedures
397  *
398  ****************************************************************/
399 
400 static void
RulerClassInit()401 RulerClassInit()
402 {
403     XtAddConverter( XtRString, XtRGravity, XmuCvtStringToGravity,
404 	NULL, (Cardinal)0) ;
405     XtSetTypeConverter( XtRString, XtRLabelStyle, CvtStringToLabelStyle,
406 	NULL, (Cardinal)0, XtCacheNone, NULL) ;
407 }
408 
409 
410 
411 	/* Init a newly created ruler widget.  Compute height of ruler
412 	 * and optionally compute size of widget. */
413 
414 
415 static void
RulerInit(request,new,args,num_args)416 RulerInit(request, new, args, num_args)
417     Widget request, new;
418     ArgList args;
419     Cardinal *num_args;
420 {
421     MwRulerWidget newRw = (MwRulerWidget)new;
422 
423     /* GC allocation is deferred until XtRealize() */
424 
425     newRw->ruler.foregroundGC =
426     newRw->ruler.fracGC =
427     newRw->ruler.pointerGC = None ;
428 
429     newRw->ruler.needs_layout = True ;
430 
431     if( newRw->ruler.fracFont == NULL )
432       newRw->ruler.fracFont = newRw->ruler.font ;
433 
434     switch(newRw->ruler.orientation) {
435       case NorthWestGravity:
436       case NorthEastGravity:
437         newRw->ruler.orientation = NorthGravity ;
438 	break ;
439       case SouthWestGravity:
440       case SouthEastGravity:
441         newRw->ruler.orientation = SouthGravity ;
442 	break ;
443     }
444 
445     /* if size not explicitly set, set it to our preferred size now. */
446 
447     if( request->core.width == 0 || request->core.height == 0 )
448     {
449       Dimension	w=100,h=100 ;
450       PreferredSize(newRw, &w, &h) ;
451       if( request->core.width == 0 ) new->core.width = w ;
452       if( request->core.height == 0 ) new->core.height = h ;
453       XtClass(new)->core_class.resize(new) ;
454     }
455 }
456 
457 
458 
459 
460 	/* Called when ruler widget first realized.  Create the window
461 	 * and allocate the GCs
462 	 */
463 
464 static	void
RulerRealize(w,valueMask,attributes)465 RulerRealize(w, valueMask, attributes)
466 	Widget w;
467 	Mask *valueMask;
468 	XSetWindowAttributes *attributes;
469 {
470 	MwRulerWidget rw = (MwRulerWidget) w;
471 	Display	*dpy = XtDisplay(w) ;
472 
473 	(*SuperClass->core_class.realize)(w, valueMask, attributes) ;
474 
475 	RulerAllocGCs(rw) ;
476 
477 	if( rw->ruler.cursor == None )
478 	  switch( rw->ruler.orientation ) {
479 	    case NorthGravity:
480 	    case SouthGravity:
481 	      rw->ruler.cursor = XCreateFontCursor(dpy, XC_sb_h_double_arrow);
482 	      break ;
483 	    case WestGravity:
484 	    case EastGravity:
485 	      rw->ruler.cursor = XCreateFontCursor(dpy, XC_sb_v_double_arrow);
486 	      break ;
487 	  }
488 
489 	if( XtIsSensitive(w) )
490 	  XDefineCursor(dpy, XtWindow(w), rw->ruler.cursor) ;
491 	rw->ruler.indexPm = XCreatePixmap( dpy, XtWindow(w),
492 		PS2, PS2, w->core.depth ) ;
493 	rw->ruler.indexSaved = False ;
494 }
495 
496 
497 
498 static	void
RulerDestroy(w)499 RulerDestroy(w)
500 	Widget	w ;
501 {
502 	MwRulerWidget rw = (MwRulerWidget)w ;
503 	RulerFreeGCs(rw) ;
504 	XFreePixmap(XtDisplay(w), rw->ruler.indexPm) ;
505 }
506 
507 
508 	/* Parent has resized us.  This will require that the ruler be
509 	 * laid out again.
510 	 */
511 
512 static void
RulerResize(w)513 RulerResize(w)
514 	Widget	w;
515 {
516 	MwRulerWidget	rw = (MwRulerWidget) w;
517 	double		v0 = rw->ruler.minValue ;
518 	XRectangle	rect ;
519 
520 	RulerStepDiv(rw) ;
521 
522 	rect.x = rect.y = 0 ;
523 	rect.width = rw->core.width ;
524 	rect.height = rw->core.height ;
525 
526 	rw->ruler.v0 = v0 ;
527 	rw->ruler.l0 = 0 ;
528 
529 	rw->ruler.needs_layout = False ;
530 }
531 
532 
533 
534 	/* utility: generate a label.  Value is v + n/d, expressed
535 	 * as either a fraction or a decimal.  V might be negative;
536 	 * numerator,denominator are not.
537 	 *
538 	 * Three cases:  Whole number, decimal output, fraction output
539 	 */
540 
541 static	void
fracStr(rw,line,v,numerator,denominator,sign)542 fracStr(rw, line, v, numerator,denominator, sign)
543 	MwRulerWidget	rw ;
544 	char		line[128] ;
545 	int		v, numerator, denominator ;
546 	int		sign ;
547 {
548 	if( numerator >= denominator ) {
549 	  v += numerator/denominator ;
550 	  numerator %= denominator ;
551 	}
552 	if( rw->ruler.labelStyle == FRACTION && v*sign < 0 && numerator > 0 )
553 	  numerator = denominator - numerator ;
554 
555 	if( numerator == 0 || denominator <= 0 )
556 	  sprintf(line, "%d", v) ;
557 	else if( rw->ruler.labelStyle == DECIMAL )
558 	  sprintf(line, "%g", v+(double)sign*numerator/denominator) ;
559 	else
560 	{
561 	  int i, j, k ;
562 	  i = numerator; j=denominator ;
563 	  while( (k=i%j) > 0 ) {i=j; j=k;}	/* GCD */
564 	  sprintf(line, "%d/%d", numerator/j,denominator/j) ;
565 	}
566 }
567 
568 
569 	/* utility: return the width that a fractional label will require */
570 
571 static	int
fracWid(rw,v,divisions)572 fracWid(rw, v, divisions)
573 	MwRulerWidget	rw ;
574 	int		v, divisions ;
575 {
576 	char	line[128] ;
577 
578 	fracStr(rw, line, v, divisions-1,divisions, 1) ;
579 	return 2 * XTextWidth(rw->ruler.fracFont, line, strlen(line)) ;
580 }
581 
582 
583 static	int
rulerWid(rw,v,divisions)584 rulerWid(rw, v, divisions)
585 	MwRulerWidget	rw ;
586 	int		v, divisions ;
587 {
588 	return v ;
589 }
590 
591 
592 	/* Utility: increase step until it's more than sm */
593 
594 static	int
minStep(step,sm,scale)595 minStep(step, sm, scale)
596 	int	step, sm ;
597 	double	scale ;
598 {
599 	while( step*scale < sm ) {
600 	  step *= 2 ;
601 	  if( step*scale < sm )
602 	    step = step*5/2 ;
603 	  if( step*scale < sm )
604 	    step *= 2 ;
605 	}
606 	return step ;
607 }
608 
609 	/* Utility: divide divisions until func()*divisions <= scale
610 	 * where func() returns the space required by a single
611 	 * tic mark or label.
612 	 */
613 
614 static	int
maxDiv(rw,divisions,width,func,arg)615 maxDiv(rw, divisions, width, func, arg)
616 	MwRulerWidget rw ;
617 	int	divisions ;	/* initial # of divisions */
618 	int	width ;		/* ruler scale factor, pixels */
619 	int	(*func)() ;	/* returns width of one division */
620 	int	arg ;		/* passed to func */
621 {
622 	int	i ;
623 
624 	/* This is more complicated than you'd think it should
625 	 * be.  We need to reduce the number of divisions to
626 	 * make room between the tic marks.  Try dividing divisions
627 	 * by small reasonable numbers (where reasonable is defined
628 	 * to mean that it divides evenly) until we have enough
629 	 * room.  If that doesn't work, divide by *something* and
630 	 * try again.
631 	 */
632 
633 
634 	for(;;)
635 	{
636 	  for(i=2; i<=10; ++i)
637 	    if( divisions % i == 0 &&
638 		func(rw, arg, divisions/i)*divisions/i <= width )
639 	      return divisions/i ;
640 
641 	  /* can't keep trying small integers forever, try scaling
642 	   * it down.
643 	   */
644 
645 	  if( divisions % 2 == 0 )
646 	    divisions /= 2 ;
647 	  else if( divisions % 3 == 0 )
648 	    divisions /= 3 ;
649 	  else if( divisions % 5 == 0 )
650 	    divisions /= 5 ;
651 	  else if( divisions % 7 == 0 )
652 	    divisions /= 7 ;
653 	  else if( divisions % 11 == 0 )
654 	    divisions /= 11 ;
655 	  else
656 	    divisions /= 2 ;	/* just force it */
657 	}
658 }
659 
660 
661 
662 	/* Redraw entire Ruler widget */
663 
664 static	void
RulerExpose(w,event,region)665 RulerExpose(w, event, region)
666 	Widget	w ;
667 	XEvent	*event ;
668 	Region	region ;
669 {
670 	MwRulerWidget	rw = (MwRulerWidget) w;
671 	Position	px,py ;
672 	Dimension	wid, hgt ;
673 
674 	if( rw->ruler.needs_layout )
675 	  XtClass(w)->core_class.resize(w) ;
676 
677 	/* Make sure we erase the index mark first */
678 
679 	if( rw->ruler.indexSaved ) {
680 	  indexPmPos(rw, &px, &py, &wid,&hgt) ;
681 	  XClearArea(XtDisplay(w), XtWindow(w), px,py, wid,hgt, False) ;
682 	}
683 
684 	RulerDraw(rw, 0,rw->ruler.length) ;
685 	drawPointer(rw) ;
686 }
687 
688 
689 
690 static	void
RulerDraw(rw,x1,x2)691 RulerDraw(rw, x1,x2)
692 	MwRulerWidget	rw ;
693 	int		x1,x2 ;
694 {
695 	char	line[128] ;
696 	int	lstep, mstep ;
697 	long	x ;
698 	int	ty = 0,lx = 0,ly = 0;
699 	int	j ;
700 	int	x0, x3 ;
701 	int	ps ;				/* pixel step */
702 	double	scale = rw->ruler.scale ;
703 	double	ascale = abs(scale) ;
704 	int	sign = scale >= 0 ? 1 : -1 ;
705 	double	v0 = rw->ruler.v0 ;
706 	double	minv = rw->ruler.minValue ;
707 	int	tstep ;
708 
709 	if( !XtIsRealized((Widget)rw) )
710 	  return ;
711 
712 
713 	/* find our maximum range */
714 
715 	x0 = 0 ;
716 	x3 = x0 + rw->ruler.length - 1 ;
717 
718 	/* figure out where labels & tic marks go */
719 
720 	switch( rw->ruler.orientation ) {
721 	  case NorthGravity:
722 	    ly = 2 + fontAscent(rw->ruler.font) ;
723 	    ty = rw->core.height - 1 ;
724 	    break ;
725 	  case SouthGravity:
726 	    ly = rw->core.height - 2 ;
727 	    ty = 0 ;
728 	    break ;
729 	  case WestGravity:
730 	    ly = 2 ;
731 	    ty = rw->core.width - 1 ;
732 	    break ;
733 	  case EastGravity:
734 	    ly = rw->core.width - 2 ;
735 	    ty = 0 ;
736 	    break ;
737 	}
738 
739 	x1 = max(x1,x0) ;
740 	x2 = min(x2,x3) ;
741 
742 	if( x2 < x1 ) return ;
743 
744 
745 	if( (mstep = rw->ruler.mStep) > 0 )
746 	{
747 	  int	mdiv = rw->ruler.mDiv ;		/* minor tic marks/major tic */
748 	  double t ;
749 	  t = sign > 0 ? ifloor(minv/mstep)*mstep : iceil(minv/mstep)*mstep ;
750 	  ps = mstep * ascale ;
751 	  tstep = sign * mstep ;
752 	  do {
753 	    x = (t-v0) * scale ;
754 	    x += rw->ruler.l0 ;
755 	    drawTic(rw, (int)x,ty, TIC_LEN, x1,x2) ;
756 	    if( mdiv > 1  &&  x <= x2  &&  x+ps >= x1 )
757 	      drawTics(rw, (int)x,ty, ps, mdiv, TIC_LEN-2, x1,x2) ;
758 	    t += tstep ;
759 	  } while( x <= x2 ) ;
760 	}
761 
762 
763 	if( rw->ruler.font != NULL && (lstep = rw->ruler.lStep) > 0 )
764 	{
765 	  GC gc = rw->ruler.foregroundGC ;
766 	  GC fgc = rw->ruler.fracGC ;
767 	  int	ldiv = rw->ruler.lDiv ;		/* minor labels/major label */
768 	  double t ;
769 	  t = sign > 0 ? ifloor(minv/lstep)*lstep : iceil(minv/lstep)*lstep ;
770 	  ps = lstep * ascale ;
771 	  tstep = sign * lstep ;
772 	  do {
773 	    x = (t-v0) * scale ;
774 	    x += rw->ruler.l0 ;
775 	    sprintf(line, "%d", (int)t) ;
776 	    drawLabel(rw, (int)x,ly, line, gc, rw->ruler.font) ;
777 	    for(j=1; j<ldiv; ++j) {
778 	      lx = x + (ps*j)/ldiv ;
779 	      if( lx-rw->ruler.txtWid <= x2  &&  lx+rw->ruler.txtWid >= x1 ) {
780 		fracStr(rw, line, (int)t, j*lstep,ldiv, sign) ;
781 		drawLabel(rw, lx,ly, line, fgc, rw->ruler.fracFont) ;
782 	      }
783 	    }
784 	    t += tstep ;
785 	  } while( x <= x2 ) ;
786 	}
787 }
788 
789 
790 
791 
792 	/* Draw n tic marks, distributed over 'dx' pixels */
793 
794 static	void
drawTics(rw,x,y,dx,n,tic_len,x1,x2)795 drawTics(rw, x,y,dx,n, tic_len, x1,x2)
796 	MwRulerWidget rw ;
797 	int	x, dx ;
798 	int	y ;
799 	int	n, tic_len ;
800 	int	x1,x2 ;
801 {
802 	int	i ;
803 	int	x0 ;
804 	int	ps, pn, pd, cnt ;	/* pixel step = ps+pn/pd */
805 
806 	tic_len = max(tic_len,2) ;
807 
808 	/* into how many parts do we divide this region? */
809 
810 	if( n % 2 == 0 ) pd = 2 ;
811 	else if( n % 3 == 0 ) pd = 3 ;
812 	else if( n % 5 == 0 ) pd = 5 ;
813 	else pd = n ;
814 
815 	ps = dx/pd ;
816 	pn = dx%pd ;
817 
818 	x0 = x ;
819 	cnt = -pd/2 ;
820 	for(i=0; i<pd; ++i)
821 	{
822 	  if( i > 0 )
823 	    drawTic(rw, x0,y, tic_len, x1,x2) ;
824 	  if( pd < n )
825 	    drawTics(rw, x0,y,ps,n/pd, tic_len-2, x1,x2) ;
826 	  x0 += ps ;
827 	  cnt += pn ; if(cnt>=0) { cnt -= pd ; ++x0 ; }
828 	}
829 }
830 
831 
832 static	void
drawTic(rw,x,y,tic_len,x1,x2)833 drawTic(rw, x,y, tic_len, x1,x2)
834     MwRulerWidget	rw ;
835     int		x,y, tic_len;
836     int		x1,x2 ;
837 {
838     Display	*dpy = XtDisplay((Widget)rw) ;
839     Window	win = XtWindow((Widget)rw) ;
840     GC		gc = rw->ruler.foregroundGC ;
841 
842     if( x < x1 || x > x2 ) return ;
843 
844     switch( rw->ruler.orientation ) {
845       case NorthGravity: XDrawLine(dpy,win,gc, x,y, x,y-tic_len) ; break ;
846       case SouthGravity: XDrawLine(dpy,win,gc, x,y, x,y+tic_len) ; break ;
847       case EastGravity:  XDrawLine(dpy,win,gc, y,x, y+tic_len,x) ; break ;
848       case WestGravity:  XDrawLine(dpy,win,gc, y,x, y-tic_len,x) ; break ;
849     }
850 }
851 
852 static	void
drawLabel(rw,x,y,str,gc,font)853 drawLabel(rw, x,y, str, gc, font)
854     MwRulerWidget	rw ;
855     int		x,y ;
856     String	str ;
857     GC		gc ;
858     XFontStruct	*font ;
859 {
860 	Display	*dpy = XtDisplay((Widget)rw) ;
861 	Window	win = XtWindow((Widget)rw) ;
862 	int	len = strlen(str) ;
863 	int	wid ;
864 
865 	switch( rw->ruler.orientation ) {
866 	  case NorthGravity:
867 	  case SouthGravity:
868 	    wid = XTextWidth(font, str, len) ;
869 	    XDrawString(dpy,win,gc, x-wid/2,y, str,len) ;
870 	    break ;
871 	  case WestGravity:
872 	    XDrawString(dpy,win,gc, y, x+fontAscent(font)/2, str,len) ;
873 	    break ;
874 	  case EastGravity:
875 	    wid = XTextWidth(font, str, len) ;
876 	    XDrawString(dpy,win,gc, y-wid, x+fontAscent(font)/2,
877 		str,len) ;
878 	    break ;
879 	}
880 }
881 
882 
883 	/* Utility: return proper base position of indexPm for ruler */
884 
885 static	void
indexPmPos(rw,x,y,wid,hgt)886 indexPmPos(rw, x, y, wid,hgt)
887     MwRulerWidget	rw ;
888     Position	*x, *y ;
889     Dimension	*wid, *hgt ;
890 {
891     *x = *y = rw->ruler.iValue - POINTER_SIZE ;
892 
893     switch( rw->ruler.orientation ) {
894       case NorthGravity:
895         *y = rw->core.height - POINTER_SIZE - 3 ;
896 	*wid = PS2 ;
897 	*hgt = POINTER_SIZE+1 ;
898 	break ;
899       case SouthGravity:
900         *y = 3 ;
901 	*wid = PS2 ;
902 	*hgt = POINTER_SIZE+1 ;
903 	break ;
904       case WestGravity:
905         *x = rw->core.width - POINTER_SIZE - 3 ;
906 	*wid = POINTER_SIZE+1 ;
907 	*hgt = PS2 ;
908 	break ;
909       case EastGravity:
910         *x = 3 ;
911 	*wid = POINTER_SIZE ;
912 	*hgt = PS2+1 ;
913 	break ;
914     }
915 }
916 
917 
918 static	void
drawPointer(rw)919 drawPointer(rw)
920 	MwRulerWidget	rw ;
921 {
922     Display	*dpy = XtDisplay((Widget)rw) ;
923     Window	win = XtWindow((Widget)rw) ;
924     GC		gc = rw->ruler.pointerGC ;
925     XPoint	pts[4] ;
926     Position	x,y,d = 0;
927     Dimension	wid,hgt ;
928 
929     if( rw->ruler.iValue < -POINTER_SIZE  ||  !rw->ruler.showPointer )
930       return ;
931 
932     indexPmPos(rw, &x, &y, &wid,&hgt) ;
933 
934     XCopyArea(dpy,win, rw->ruler.indexPm, gc, x,y, wid, hgt, 0,0) ;
935     rw->ruler.indexSaved = True ;
936 
937     x = y = rw->ruler.iValue ;
938 
939     switch( rw->ruler.orientation ) {
940       case NorthGravity:
941 	y = rw->core.height - 3 ;
942 	d = POINTER_SIZE ;
943 	break ;
944       case SouthGravity:
945 	y = 3 ;
946 	d = -POINTER_SIZE ;
947 	break ;
948       case WestGravity:
949 	x = rw->core.width - 3 ;
950 	d = POINTER_SIZE ;
951 	break ;
952       case EastGravity:
953 	x = 3 ;
954 	d = -POINTER_SIZE ;
955 	break ;
956     }
957 
958     switch( rw->ruler.orientation ) {
959       case NorthGravity:
960       case SouthGravity:
961 	pts[0].x = x ;		pts[0].y = y ;
962 	pts[1].x = x-d ;	pts[1].y = y-d ;
963 	pts[2].x = x+d ;	pts[2].y = y-d ;
964 	pts[3].x = x ;		pts[3].y = y ;
965 	break ;
966       case EastGravity:
967       case WestGravity:
968 	pts[0].y = y ;		pts[0].x = x ;
969 	pts[1].y = y-d ;	pts[1].x = x-d ;
970 	pts[2].y = y+d ;	pts[2].x = x-d ;
971 	pts[3].y = y ;		pts[3].x = x ;
972 	break ;
973     }
974 
975     XDrawLines(dpy, win, gc, pts, 4, CoordModeOrigin) ;
976 }
977 
978 
979 static	void
undrawPointer(rw)980 undrawPointer(rw)
981     MwRulerWidget	rw ;
982 {
983     Display	*dpy = XtDisplay((Widget)rw) ;
984     Window	win = XtWindow((Widget)rw) ;
985     GC		gc = rw->ruler.pointerGC ;
986     Position	x,y ;
987     Dimension	wid,hgt ;
988 
989     if( !XtIsRealized((Widget)rw) || !rw->ruler.indexSaved)
990       return ;
991 
992     indexPmPos(rw, &x, &y, &wid,&hgt) ;
993 
994     XCopyArea(dpy, rw->ruler.indexPm, win, gc, 0,0, wid,hgt, x,y) ;
995 
996     rw->ruler.indexSaved = False ;
997 }
998 
999 
1000 
1001 	/* Called when any Ruler widget resources are changed. */
1002 
1003 static Boolean
RulerSetValues(old,request,new,args,num_args)1004 RulerSetValues(old, request, new, args, num_args)
1005 	Widget	old, request, new;
1006 	ArgList	args;
1007 	Cardinal *num_args;
1008 {
1009 	MwRulerWidget oldRw = (MwRulerWidget) old ;
1010 	MwRulerWidget rw = (MwRulerWidget) new ;
1011 	Boolean	needRedraw = False ;
1012 
1013 
1014 	/* Changing font, scale or minValue may cause a size change */
1015 	if( rw->ruler.font != oldRw->ruler.font  ||
1016 	    rw->ruler.minValue != oldRw->ruler.minValue ||
1017 	    rw->ruler.scale != oldRw->ruler.scale )
1018 	{
1019 	  PreferredSize(rw, &rw->core.width, &rw->core.height) ;
1020 	  rw->ruler.needs_layout = True ;
1021 	  needRedraw = True ;
1022 	}
1023 
1024 	if( rw->ruler.labelStep != oldRw->ruler.labelStep ||
1025 	    rw->ruler.labelDiv != oldRw->ruler.labelDiv ||
1026 	    rw->ruler.labelStyle != oldRw->ruler.labelStyle ||
1027 	    rw->ruler.markStep != oldRw->ruler.markStep ||
1028 	    rw->ruler.markDiv != oldRw->ruler.markDiv )
1029 	  rw->ruler.needs_layout = needRedraw = True ;
1030 
1031 	/* TODO: if any color changes, need to recompute GCs and redraw */
1032 
1033 	if( rw->ruler.foreground != oldRw->ruler.foreground )
1034 	{
1035 	  if( XtIsRealized(new) ) {
1036 	    XtReleaseGC(new, rw->ruler.foregroundGC) ;
1037 	    XtReleaseGC(new, rw->ruler.fracGC) ;
1038 	    RulerAllocFgGC(rw) ;
1039 	  }
1040 	  needRedraw = True ;
1041 	}
1042 
1043 	if( rw->ruler.pointerColor != oldRw->ruler.pointerColor )
1044 	{
1045 	  if( XtIsRealized(new) ) {
1046 	    XtReleaseGC(new, rw->ruler.pointerGC) ;
1047 	    RulerAllocPointerGC(rw) ;
1048 	  }
1049 	  needRedraw = True ;
1050 	}
1051 
1052 	if( rw->ruler.value != oldRw->ruler.value  &&
1053 	    rw->ruler.iValue == oldRw->ruler.iValue )
1054 	{
1055 	  rw->ruler.iValue = (rw->ruler.value - rw->ruler.minValue) *
1056 		rw->ruler.scale ;
1057 	  needRedraw = True ;
1058 	}
1059 
1060 	if( rw->ruler.iValue != oldRw->ruler.iValue  &&
1061 	    rw->ruler.value == oldRw->ruler.value &&
1062 	    rw->ruler.scale > 0 )
1063 	{
1064 	  if( rw->ruler.scale == 0. ) rw->ruler.scale = 1.0 ;
1065 	  rw->ruler.value = rw->ruler.iValue / rw->ruler.scale +
1066 	  	rw->ruler.minValue ;
1067 	  needRedraw = True ;
1068 	}
1069 
1070 	if( rw->core.background_pixel != oldRw->core.background_pixel ||
1071 	    rw->core.background_pixmap != oldRw->core.background_pixmap ||
1072 	    rw->ruler.showPointer != oldRw->ruler.showPointer )
1073 	{
1074 	  needRedraw = True ;
1075 	}
1076 
1077 	/* not allowed to change orientation */
1078 	rw->ruler.orientation = oldRw->ruler.orientation ;
1079 
1080 	if( rw->ruler.cursor != oldRw->ruler.cursor  ||
1081 	    XtIsSensitive(old) != XtIsSensitive(new) )
1082 	{
1083 	  if( XtIsSensitive(new) )
1084 	    XDefineCursor(XtDisplay(new), XtWindow(new), rw->ruler.cursor) ;
1085 	  else
1086 	    XDefineCursor(XtDisplay(new), XtWindow(new), None) ;
1087 	}
1088 
1089 	return needRedraw ;
1090 }
1091 
1092 
1093 
1094 
1095 /*
1096  * Return preferred size.  We only care about one dimension.
1097  */
1098 
1099 static XtGeometryResult
RulerQueryGeometry(w,intended,preferred)1100 RulerQueryGeometry(w, intended, preferred)
1101 	Widget		w;
1102 	XtWidgetGeometry *intended, *preferred;
1103 {
1104 register MwRulerWidget rw = (MwRulerWidget)w ;
1105 
1106 	if( intended->width == w->core.width  &&
1107 	    intended->height == w->core.height )
1108 	  return XtGeometryNo ;
1109 
1110 	PreferredSize(rw, &preferred->width, &preferred->height) ;
1111 
1112 	switch( rw->ruler.orientation ) {
1113 	  case NorthGravity:
1114 	  case SouthGravity:
1115 	    preferred->request_mode = CWHeight ;
1116 	    if( !(intended->request_mode & CWHeight) ||
1117 		intended->height >= preferred->height )
1118 	      return XtGeometryYes;
1119 	    break ;
1120 	  default:
1121 	    preferred->request_mode = CWWidth ;
1122 	    if( !(intended->request_mode & CWWidth) ||
1123 		intended->width >= preferred->width )
1124 	      return XtGeometryYes;
1125 	    break ;
1126 	}
1127 
1128 	return XtGeometryAlmost;
1129 }
1130 
1131 
1132 
1133 
1134 
1135 
1136 /****************************************************************
1137  *
1138  * Action Procedures
1139  *
1140  ****************************************************************/
1141 
1142 	/* User clicks on ruler */
1143 
1144 static	void
StartScroll(w,event,params,num_params)1145 StartScroll(w, event, params, num_params)
1146 	Widget	w ;
1147 	XEvent	*event ;
1148 	String	*params ;
1149 	Cardinal *num_params ;
1150 {
1151 	MwRulerWidget	rw = (MwRulerWidget) w ;
1152 	MwRulerReport	rpt ;
1153 	Position	x,y ;
1154 	ExtractPosition(event, &x,&y) ;
1155 	switch( rw->ruler.orientation ) {
1156 	  case NorthGravity:
1157 	  case SouthGravity: rw->ruler.drag = x ; break ;
1158 	  default: rw->ruler.drag = y ; break ;
1159 	}
1160 
1161 	rpt.what = START ;
1162 	rpt.minValue = rw->ruler.minValue ;
1163 	XtCallCallbackList(w, rw->ruler.callbacks, (XtPointer)&rpt) ;
1164 }
1165 
1166 
1167 	/* User drags ruler. */
1168 
1169 static	void
Drag(w,event,params,num_params)1170 Drag(w, event, params, num_params)
1171 	Widget	w ;
1172 	XEvent	*event ;
1173 	String	*params ;
1174 	Cardinal *num_params ;
1175 {
1176 	MwRulerWidget	rw = (MwRulerWidget) w ;
1177 	MwRulerReport	rpt ;
1178 	int		dp ;
1179 	Position	x,y ;
1180 
1181 	ExtractPosition(event, &x,&y) ;
1182 	switch( rw->ruler.orientation ) {
1183 	  case NorthGravity:
1184 	  case SouthGravity:
1185 	    rpt.dx = dp = x - rw->ruler.drag ;
1186 	    rpt.dy = 0 ;
1187 	    rw->ruler.drag = x ;
1188 	    break ;
1189 	  default:
1190 	    rpt.dx = 0 ;
1191 	    rpt.dy = dp = y - rw->ruler.drag ;
1192 	    rw->ruler.drag = y ;
1193 	    break ;
1194 	}
1195 	iScroll(rw,dp) ;
1196 
1197 	rpt.what = SCROLL ;
1198 	rpt.minValue = rw->ruler.minValue ;
1199 	XtCallCallbackList(w, rw->ruler.callbacks, (XtPointer)&rpt) ;
1200 }
1201 
1202 
1203 	/* User releases ruler. */
1204 
1205 static	void
EndScroll(w,event,params,num_params)1206 EndScroll(w, event, params, num_params)
1207 	Widget	w ;
1208 	XEvent	*event ;
1209 	String	*params ;
1210 	Cardinal *num_params ;
1211 {
1212 	MwRulerWidget	rw = (MwRulerWidget) w ;
1213 	MwRulerReport	rpt ;
1214 
1215 	rpt.what = STOP ;
1216 	rpt.dx = rpt.dy = 0 ;
1217 	rpt.minValue = rw->ruler.minValue ;
1218 	XtCallCallbackList(w, rw->ruler.callbacks, (XtPointer)&rpt) ;
1219 }
1220 
1221 
1222 	/* User presses a key */
1223 
1224 static	void
Scroll(w,event,params,num_params)1225 Scroll(w, event, params, num_params)
1226 	Widget	w ;
1227 	XEvent	*event ;
1228 	String	*params ;
1229 	Cardinal *num_params ;
1230 {
1231 	MwRulerWidget	rw = (MwRulerWidget)w ;
1232 	MwRulerReport	rpt ;
1233 	String	amt ;
1234 	int	delta = 1 ;	/* pixels to scroll */
1235 	double	scale = rw->ruler.scale ;
1236 	Boolean	doIscroll = True ;
1237 
1238 	if( *num_params < 1 || scale == 0. ) return ;
1239 
1240 	amt = params[0] ;
1241 
1242 	if( *amt == '-' ) {
1243 	  delta = -1 ;
1244 	  ++amt ;
1245 	}
1246 
1247 	if( isdigit(*amt) )
1248 	  delta *= atoi(amt) ;
1249 	else
1250 	  switch(*amt) {
1251 	    case 'p': delta *= rw->ruler.length/2 ; break ;
1252 	    case 't':
1253 	      /* special case: we're not interested in an integer
1254 	       * number of pixels, but rather one tic mark.  If
1255 	       * the scale factor is an integer, then fine.  Otherwise,
1256 	       * we'd better do a redisplay.
1257 	       */
1258 	      if( scale < 0 ) delta = -delta ;
1259 	      if( scale != (int)scale ) {
1260 		MwRulerSetMin(w, rw->ruler.minValue - delta) ;
1261 		doIscroll = False ;
1262 	      }
1263 	      delta *= scale ;
1264 	      break ;
1265 	    default: return ;
1266 	  }
1267 
1268 	if( doIscroll )
1269 	  iScroll(rw, delta) ;
1270 
1271 	rpt.what = STOP ;
1272 	switch( rw->ruler.orientation ) {
1273 	  case NorthGravity:
1274 	  case SouthGravity: rpt.dx = delta ; rpt.dy = 0 ; break ;
1275 	  default: rpt.dx = 0 ; rpt.dy = delta ;
1276 	}
1277 	rpt.minValue = rw->ruler.minValue ;
1278 	XtCallCallbackList(w, rw->ruler.callbacks, (XtPointer)&rpt) ;
1279 }
1280 
1281 
1282 	/* Not normally used, available to users.
1283 	 * 1 arg: arg is minvalue, place at left/top edge
1284 	 * 2 args: arg 2 is pixel location for minvalue
1285 	 *   negative values are taken relative to other edge.
1286 	 */
1287 
1288 static	void
ScrollTo(w,event,params,num_params)1289 ScrollTo(w, event, params, num_params)
1290 	Widget	w ;
1291 	XEvent	*event ;
1292 	String	*params ;
1293 	Cardinal *num_params ;
1294 {
1295 	MwRulerWidget	rw = (MwRulerWidget)w ;
1296 	MwRulerReport	rpt ;
1297 	double		scale = rw->ruler.scale ;
1298 	double		minValue, oldMin ;
1299 	int		pixValue, delta ;
1300 
1301 	if( *num_params < 1 || scale == 0. ) return ;
1302 
1303 	minValue = atof(params[0]) ;
1304 
1305 	if( *num_params >= 2 ) {
1306 	  pixValue = atoi(params[1]) ;
1307 	  if( params[1][0] == '-' )
1308 	    pixValue += rw->ruler.length ;
1309 	}
1310 	else
1311 	  pixValue = 0 ;
1312 
1313 	oldMin = rw->ruler.minValue ;
1314 
1315 	MwRulerSetMinPosition(w, minValue, pixValue) ;
1316 
1317 	oldMin -= rw->ruler.minValue ;
1318 	delta = scale * oldMin ;
1319 
1320 	rpt.what = STOP ;
1321 	switch( rw->ruler.orientation ) {
1322 	  case NorthGravity:
1323 	  case SouthGravity: rpt.dx = delta ; rpt.dy = 0 ; break ;
1324 	  default: rpt.dx = 0 ; rpt.dy = delta ;
1325 	}
1326 	rpt.minValue = rw->ruler.minValue ;
1327 	XtCallCallbackList(w, rw->ruler.callbacks, (XtPointer)&rpt) ;
1328 }
1329 
1330 
1331 
1332 	/* Utility: scroll ruler by an integer number of pixels */
1333 
1334 static	void
iScroll(rw,dp)1335 iScroll(rw, dp)
1336 	MwRulerWidget	rw ;
1337 	int		dp ;
1338 {
1339     Widget w = (Widget)rw ;
1340     double scale = rw->ruler.scale ;
1341     int	x1,x2 ;
1342 
1343     if( dp == 0 ) return ;
1344 
1345     if( scale != 0. )
1346       rw->ruler.minValue -= (double)dp/scale ;
1347 
1348     /* make sure our base point stays in range. */
1349 
1350     if( dp > MAX_L0 || dp < -MAX_L0  ||
1351 	(rw->ruler.l0 += dp) > MAX_L0 || rw->ruler.l0 < -MAX_L0 )
1352     {
1353       /* better do a full redraw */
1354       MwRulerSetMin(w, rw->ruler.minValue) ;
1355       return ;
1356     }
1357 
1358     if( XtIsRealized((Widget)rw) )
1359     {
1360 	Display	*dpy = XtDisplay(w) ;
1361 	Window	win = XtWindow(w) ;
1362 	GC	gc = rw->ruler.foregroundGC ;
1363 	int	l = rw->ruler.length ;
1364 	int	sx,sy,w,h,dx,dy ;
1365 	int	cx,cy,cw,ch ;
1366 
1367 	undrawPointer(rw) ;
1368 	switch( rw->ruler.orientation ) {
1369 	  case NorthGravity:
1370 	  case SouthGravity:
1371 	    dy = sy = cy = 0 ;
1372 	    h = ch = rw->core.height ;
1373 	    if( dp > 0 ) { cx = sx = 0 ; dx = dp ; w = l-dp ; cw = dp ; }
1374 	    else { sx = -dp ; dx = 0 ; w = l+dp ; cw = -dp ; cx = l+dp ; }
1375 	    x1 = cx ;
1376 	    x2 = cx + abs(dp) ;
1377 	    break ;
1378 	  default:
1379 	    dx = sx = cx = 0 ;
1380 	    w = cw = rw->core.width ;
1381 	    if( dp > 0 ) { cy = sy = 0 ; dy = dp ; h = l-dp ; ch = dp ; }
1382 	    else { sy = -dp ; dy = 0 ; h = l+dp ; ch = -dp ; cy = l+dp ; }
1383 	    x1 = cy ;
1384 	    x2 = cy + abs(dp) ;
1385 	    break ;
1386 	}
1387 	XCopyArea(dpy,win,win,gc, sx,sy, w,h, dx,dy) ;
1388 	XClearArea(dpy,win, cx,cy,cw,ch, False) ;
1389 
1390 	RulerDraw(rw, x1,x2) ;
1391 	drawPointer(rw) ;
1392     }
1393 }
1394 
1395 
1396 
1397 	/* Utility: get x,y coordinates from an event */
1398 
1399 static void
ExtractPosition(event,x,y)1400 ExtractPosition (event, x, y)
1401     XEvent *event;
1402     Position *x, *y;		/* RETURN */
1403 {
1404     switch( event->type ) {
1405     case MotionNotify:
1406 	*x = event->xmotion.x;
1407 	*y = event->xmotion.y;
1408 	break;
1409     case ButtonPress:
1410     case ButtonRelease:
1411 	*x = event->xbutton.x;
1412 	*y = event->xbutton.y;
1413 	break;
1414     case KeyPress:
1415     case KeyRelease:
1416 	*x = event->xkey.x;
1417 	*y = event->xkey.y;
1418 	break;
1419     case EnterNotify:
1420     case LeaveNotify:
1421 	*x = event->xcrossing.x;
1422 	*y = event->xcrossing.y;
1423 	break;
1424     default:
1425 	*x = 0; *y = 0;
1426     }
1427 }
1428 
1429 
1430 
1431 
1432 
1433 /****************************************************************
1434  *
1435  * Public Procedures
1436  *
1437  ****************************************************************/
1438 
1439 
1440 void
MwRulerSetMinScale(w,minValue,scale)1441 MwRulerSetMinScale(w, minValue, scale)
1442 	Widget	w ;
1443 	double	minValue ;
1444 	double	scale ;
1445 {
1446 	MwRulerWidget rw = (MwRulerWidget)w ;
1447 
1448 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1449 
1450 	rw->ruler.minValue = minValue ;
1451 	rw->ruler.scale = scale ;
1452 	rw->ruler.needs_layout = True ;
1453 	rw->ruler.iValue = (rw->ruler.value - minValue) * scale ;
1454 	XClearWindow(XtDisplay(w), XtWindow(w)) ;
1455 	XtClass(w)->core_class.expose(w,NULL,None) ;
1456 }
1457 
1458 
1459 void
MwRulerSetMin(w,minValue)1460 MwRulerSetMin(w, minValue)
1461 	Widget	w ;
1462 	double	minValue ;
1463 {
1464 	MwRulerWidget rw = (MwRulerWidget)w ;
1465 
1466 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1467 
1468 	MwRulerSetMinScale(w, minValue, rw->ruler.scale) ;
1469 }
1470 
1471 
1472 void
MwRulerSetScale(w,scale)1473 MwRulerSetScale(w, scale)
1474 	Widget	w ;
1475 	double	scale ;
1476 {
1477 	MwRulerWidget rw = (MwRulerWidget)w ;
1478 
1479 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1480 
1481 	MwRulerSetMinScale(w, rw->ruler.minValue, scale) ;
1482 }
1483 
1484 
1485 void
MwRulerSetMinPosition(w,minValue,position)1486 MwRulerSetMinPosition(w, minValue, position)
1487 	Widget	w ;
1488 	double	minValue ;
1489 	int	position ;
1490 {
1491 	MwRulerWidget rw = (MwRulerWidget)w ;
1492 
1493 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1494 
1495 	if( position != 0 && rw->ruler.scale != 0. )
1496 	  minValue -= (double)position/rw->ruler.scale ;
1497 
1498 	MwRulerSetMin(w, minValue) ;
1499 }
1500 
1501 
1502 double
MwRulerGetMin(w)1503 MwRulerGetMin(w)
1504 	Widget	w ;
1505 {
1506 	MwRulerWidget rw = (MwRulerWidget)w ;
1507 
1508 	/* TODO: error message? */
1509 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return 0. ;
1510 
1511 	return rw->ruler.minValue ;
1512 }
1513 
1514 
1515 double
MwRulerGetScale(w)1516 MwRulerGetScale(w)
1517 	Widget	w ;
1518 {
1519 	MwRulerWidget rw = (MwRulerWidget)w ;
1520 
1521 	/* TODO: error message? */
1522 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return 0. ;
1523 
1524 	return rw->ruler.scale ;
1525 }
1526 
1527 
1528 void
MwRulerSetValue(w,value)1529 MwRulerSetValue(w, value)
1530 	Widget	w ;
1531 	double	value ;
1532 {
1533 	MwRulerWidget rw = (MwRulerWidget)w ;
1534 
1535 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1536 
1537 	undrawPointer(rw) ;
1538 	rw->ruler.value = value ;
1539 	rw->ruler.iValue = MwRulerValue2Position(w, value) ;
1540 	drawPointer(rw) ;
1541 }
1542 
1543 void
MwRulerSetIValue(w,value)1544 MwRulerSetIValue(w, value)
1545 	Widget	w ;
1546 	int	value ;
1547 {
1548 	MwRulerWidget rw = (MwRulerWidget)w ;
1549 
1550 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1551 
1552 	undrawPointer(rw) ;
1553 	rw->ruler.iValue = value ;
1554 	if( rw->ruler.scale > 0. )
1555 	  rw->ruler.value = MwRulerPosition2Value(w, value) ;
1556 	drawPointer(rw) ;
1557 }
1558 
1559 
1560 void
MwRulerShowPointer(w,show)1561 MwRulerShowPointer(w, show)
1562 	Widget	w ;
1563 	Bool	show ;
1564 {
1565 	MwRulerWidget rw = (MwRulerWidget)w ;
1566 
1567 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return ;
1568 
1569 	if( rw->ruler.showPointer )
1570 	  undrawPointer(rw) ;
1571 
1572 	rw->ruler.showPointer = show ;
1573 
1574 	if( show )
1575 	  drawPointer(rw) ;
1576 }
1577 
1578 
1579 double
MwRulerGetValue(w)1580 MwRulerGetValue(w)
1581 	Widget	w ;
1582 {
1583 	MwRulerWidget rw = (MwRulerWidget)w ;
1584 
1585 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return 0. ;
1586 
1587 	return rw->ruler.value ;
1588 }
1589 
1590 int
MwRulerGetIValue(w)1591 MwRulerGetIValue(w)
1592 	Widget	w ;
1593 {
1594 	MwRulerWidget rw = (MwRulerWidget)w ;
1595 
1596 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return 0 ;
1597 
1598 	return rw->ruler.iValue ;
1599 }
1600 
1601 
1602 
1603 double
MwRulerPosition2Value(w,position)1604 MwRulerPosition2Value(w, position)
1605 	Widget	w ;
1606 	int	position ;
1607 {
1608 	MwRulerWidget rw = (MwRulerWidget)w ;
1609 
1610 	if( !XtIsSubclass(w, mwRulerWidgetClass) || rw->ruler.scale ==0. )
1611 	  return 0. ;
1612 
1613 	return rw->ruler.minValue + position / rw->ruler.scale ;
1614 
1615 }
1616 
1617 
1618 int
MwRulerValue2Position(w,value)1619 MwRulerValue2Position(w, value)
1620 	Widget	w ;
1621 	double	value ;
1622 {
1623 	MwRulerWidget rw = (MwRulerWidget)w ;
1624 	int	x ;
1625 
1626 	if( !XtIsSubclass(w, mwRulerWidgetClass) ) return 0 ;
1627 
1628 	x = (value - rw->ruler.v0) * rw->ruler.scale ;
1629 	return x + rw->ruler.l0 ;
1630 }
1631 
1632 
1633 
1634 
1635 
1636 /****************************************************************
1637  *
1638  * Private Procedures
1639  *
1640  ****************************************************************/
1641 
1642 
1643 static	void
RulerAllocGCs(rw)1644 RulerAllocGCs(rw)
1645 	MwRulerWidget rw ;
1646 {
1647 	RulerAllocFgGC(rw) ;
1648 	RulerAllocPointerGC(rw) ;
1649 }
1650 
1651 
1652 static	void
RulerFreeGCs(rw)1653 RulerFreeGCs(rw)
1654 	MwRulerWidget	rw ;
1655 {
1656 	Widget w = (Widget) rw;
1657 
1658 	XtReleaseGC(w, rw->ruler.foregroundGC) ;
1659 	XtReleaseGC(w, rw->ruler.fracGC) ;
1660 	XtReleaseGC(w, rw->ruler.pointerGC) ;
1661 }
1662 
1663 
1664 
1665 
1666 
1667 
1668 
1669 	/* GEOMETRY UTILITIES */
1670 
1671 
1672 	/* Utility: compute the stepsize & number of divisions based
1673 	 * on the user's preferences and the available space.
1674 	 */
1675 
1676 static void
RulerStepDiv(rw)1677 RulerStepDiv(rw)
1678 	MwRulerWidget	rw;
1679 {
1680 	double		scale = rw->ruler.scale ;
1681 	double		v0 = rw->ruler.minValue ;
1682 	int		step ;		/* # of units to step, normally 1. */
1683 	int		divisions ;
1684 	char		line[128] ;
1685 	int		i ;
1686 
1687 
1688 	if( scale < 0. ) {
1689 	  scale = -scale ;
1690 	  v0 = -v0 ;
1691 	}
1692 
1693 	switch(rw->ruler.orientation) {
1694 	  case NorthGravity:
1695 	  case SouthGravity:
1696 	    rw->ruler.length = rw->core.width ;
1697 	    break ;
1698 	  default:
1699 	    rw->ruler.length = rw->core.height ;
1700 	    break ;
1701 	}
1702 	/* if length <= 0, assume that we haven't been sized yet,
1703 	 * so wing it.
1704 	 */
1705 	if( rw->ruler.length <= 0 )
1706 	  rw->ruler.length = scale ;
1707 
1708 
1709 	/* Find out how finely we can pack major tic marks.
1710 	 * We want at least 3 clear pixels per tic mark.
1711 	 */
1712 
1713 	step = rw->ruler.markStep ;
1714 	divisions = rw->ruler.markDiv ;
1715 	if( step > 0 && step*scale < RULER_MIN ) {
1716 	  divisions = 1 ;
1717 	  step = minStep(step, RULER_MIN, scale) ;
1718 	}
1719 	else if( divisions > 1 && divisions * RULER_MIN > scale )
1720 	  divisions = maxDiv(rw, divisions, (int)scale, rulerWid, RULER_MIN) ;
1721 
1722 	rw->ruler.mStep = step ;
1723 	rw->ruler.mDiv = divisions ;
1724 
1725 	assert(rw->ruler.mDiv >= 1 ) ;
1726 
1727 	/* Find out how closely we can pack labels.  Assume either
1728 	 * first or last label is the biggest.  Fitting minor labels
1729 	 * gets pretty tricky, especially if the ruler is horizontal;
1730 	 * the number of subdivisions affects the size of the labels,
1731 	 * which affects how many subdivisions we can have.
1732 	 */
1733 
1734 	if( rw->ruler.font != NULL && scale > 0. && rw->ruler.labelStep > 0 )
1735 	{
1736 	  double	v1 = 0.0;
1737 	  int		wid ;
1738 
1739 	  int iv0 = ifloor(v0) ;
1740 	  int iv1 ;
1741 
1742 	  switch( rw->ruler.orientation ) {
1743 	    case NorthGravity:
1744 	    case SouthGravity:
1745 	      v1 = v0 + (double) rw->ruler.length / scale ;
1746 	      iv1 = iceil(v1) ;
1747 	      sprintf(line, "%d", iv0) ;
1748 	      wid = XTextWidth(rw->ruler.font, line, strlen(line)) ;
1749 	      sprintf(line, "%d", iv1) ;
1750 	      i = XTextWidth(rw->ruler.font, line, strlen(line)) ;
1751 	      if( i > wid ) { wid = i ; iv0 = iv1 ; }
1752 	      wid *= 2 ;
1753 	      break ;
1754 	    default:
1755 	      wid = fontAscent(rw->ruler.font) * 2 ;
1756 	  }
1757 	  /* wid is now the amount of space per label, plus an equal
1758 	   * amount of blank space between.
1759 	   */
1760 	  rw->ruler.txtWid = wid/2 ;
1761 
1762 	  step = rw->ruler.labelStep ;
1763 	  divisions = rw->ruler.labelDiv ;
1764 	  if( step > 0 && step*scale < wid ) {
1765 	    divisions = 1 ;
1766 	    step = minStep(step, wid, scale) ;
1767 	  }
1768 
1769 	  else if( divisions > 1 )
1770 	  {
1771 	    /* now compute the minor labels, for horizontal rulers,
1772 	     * this loop mimics the maxDiv() function, except that
1773 	     * the label width varies with the number of divisions.
1774 	     */
1775 	    switch( rw->ruler.orientation ) {
1776 	      case NorthGravity:
1777 	      case SouthGravity:
1778 		iv1 = -max(abs(v0),abs(v1)) ;
1779 		if( fracWid(rw, iv1, divisions) * divisions > scale )
1780 		  divisions = maxDiv(rw, divisions, (int)scale, fracWid, iv1) ;
1781 		wid = fracWid(rw, iv1, divisions) ;
1782 		break ;
1783 	      default:
1784 		wid = fontAscent(rw->ruler.fracFont) * 2 ;
1785 		divisions = maxDiv(rw, divisions, (int)scale, rulerWid, wid) ;
1786 		break ;
1787 	    }
1788 	    if( (wid/=2) > rw->ruler.txtWid )
1789 	      rw->ruler.txtWid = wid ;
1790 	  }
1791 
1792 	  rw->ruler.lStep = step ;
1793 	  rw->ruler.lDiv = divisions ;
1794 	  assert(rw->ruler.lDiv >= 1 ) ;
1795 	}
1796 }
1797 
1798 
1799 static	void
PreferredSize(rw,reply_width,reply_height)1800 PreferredSize(rw, reply_width, reply_height)
1801 	MwRulerWidget	rw;
1802 	Dimension	*reply_width, *reply_height;	/* total widget size */
1803 {
1804 	int	wid ;
1805 
1806 	switch( rw->ruler.orientation ) {
1807 	  case NorthGravity:
1808 	  case SouthGravity:
1809 	    *reply_height = TIC_LEN ;
1810 	    if( rw->ruler.font != NULL )
1811 	      *reply_height += fontAscent(rw->ruler.font) + IMARGIN ;
1812 	    break ;
1813 	  default:
1814 	    /* vertical rulers are a bit tricky.  We need an estimate
1815 	     * of what the label divisions are going to be before
1816 	     * we will know how wide they will be.
1817 	     */
1818 	    RulerStepDiv(rw) ;
1819 	    wid = fracWid(rw, (int)rw->ruler.minValue, rw->ruler.lDiv) / 2 ;
1820 	    *reply_width = TIC_LEN + IMARGIN + wid ;
1821 	}
1822 }
1823 
1824 
1825 
1826 static	int
ifloor(a)1827 ifloor(a)
1828   double a ;
1829 {
1830   int rval = a ;
1831 
1832   if( a >= 0 || rval == a ) return rval ;
1833   else return rval-1 ;
1834 }
1835 
1836 
1837 static	int
iceil(a)1838 iceil(a)
1839   double a ;
1840 {
1841   int rval = a ;
1842 
1843   if( a <= 0 || rval == a ) return rval ;
1844   else return rval+1 ;
1845 }
1846 
1847 
1848 
1849 
1850 
1851 	/* Color & GC allocation.
1852 	 *
1853 	 * Ruler widgets use the following graphics contexts:
1854 	 *
1855 	 *  Foreground		ruler label text drawn this way
1856 	 *  Insensitive Fg	foreground color greyed out.
1857 	 *  Background		ruler background color
1858 	 *
1859 	 * Background GC is used for "undrawing" parts of the ruler.
1860 	 * There is no "armed" color.
1861 	 *
1862 	 *
1863 	 * GC's are defined as follows, depending on attributes and
1864 	 * window depth:
1865 	 *
1866 	 * Monochrome:
1867 	 *	Foreground = foreground color attribute or BlackPixel()
1868 	 *	Pointer = Foreground color
1869 	 *	Background = background color attribute or WhitePixel()
1870 	 *
1871 	 * Color, beNiceToColormap=true:
1872 	 *	Foreground = foreground color attribute or BlackPixel()
1873 	 *	Pointer = Foreground color
1874 	 *	Background = background color attribute or WhitePixel()
1875 	 *
1876 	 * Color, beNiceToColormap=false:
1877 	 *	Foreground = foreground color attribute or BlackPixel()
1878 	 *	Pointer = foreground color
1879 	 *	Background = background color attribute or WhitePixel()
1880 	 *
1881 	 * Special cases:
1882 	 *	If background is white,   ??
1883 	 *	if background is black,   ??
1884 	 *
1885 	 *
1886 	 * If the widget's background is solid white or solid black,
1887 	 * this code just picks some numbers.  (The choice is designed
1888 	 * to be compatibile with ThreeD interface.)
1889 	 */
1890 
1891 static	void
RulerAllocFgGC(rw)1892 RulerAllocFgGC(rw)
1893 	MwRulerWidget	rw ;
1894 {
1895 	Widget		w = (Widget) rw;
1896 	XGCValues	values ;
1897 	u_long		mask = GCForeground|GCGraphicsExposures ;
1898 
1899 	values.foreground = rw->ruler.foreground ;
1900 	values.graphics_exposures = True ;
1901 	if( rw->ruler.font != NULL ) {
1902 	  values.font = rw->ruler.font->fid ;
1903 	  mask = GCForeground|GCFont|GCGraphicsExposures ;
1904 	}
1905 
1906 	rw->ruler.foregroundGC =
1907 	  XCreateGC( XtDisplay(w), XtWindow(w), mask, &values) ;
1908 
1909 	values.font = rw->ruler.fracFont->fid ;
1910 	rw->ruler.fracGC =
1911 	  XCreateGC( XtDisplay(w), XtWindow(w), mask, &values) ;
1912 }
1913 
1914 static	void
RulerAllocPointerGC(rw)1915 RulerAllocPointerGC(rw)
1916 	MwRulerWidget	rw;
1917 {
1918 	Widget		w = (Widget) rw;
1919 	XGCValues	values ;
1920 
1921 	values.foreground = rw->ruler.pointerColor ;
1922 	values.graphics_exposures = False ;
1923 
1924 	rw->ruler.pointerGC = XCreateGC( XtDisplay(w), XtWindow(w),
1925 	    GCForeground|GCGraphicsExposures, &values) ;
1926 }
1927 
1928 
1929 
1930 static	Boolean
CvtStringToLabelStyle(dpy,args,nargs,from,to,data)1931 CvtStringToLabelStyle(dpy, args, nargs, from, to, data)
1932 	Display		*dpy ;
1933 	XrmValuePtr	args ;
1934 	Cardinal	*nargs ;
1935 	XrmValuePtr	from ;
1936 	XrmValuePtr	to ;
1937 	XtPointer	*data ;
1938 {
1939 	String	str = (String) from->addr ;
1940 
1941 	to->size = sizeof(XtLabelStyle) ;
1942 
1943 	if( XmuCompareISOLatin1(str, "fraction") == 0 )
1944 	  *(XtLabelStyle *)to->addr = FRACTION ;
1945 	else if( XmuCompareISOLatin1(str, "decimal") == 0 )
1946 	  *(XtLabelStyle *)to->addr = DECIMAL ;
1947 	else {
1948 	  XtStringConversionWarning(from->addr, "labelStyle");
1949 	  return False ;
1950 	}
1951 	return True ;
1952 }
1953