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