1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/ruler.c,v 1.20 2011/05/16 16:21:59 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_RULER_C_
22 
23 #include "tgifdefs.h"
24 
25 #include "choice.e"
26 #include "cursor.e"
27 #include "dialog.e"
28 #include "file.e"
29 #include "font.e"
30 #include "grid.e"
31 #include "mainloop.e"
32 #include "mainmenu.e"
33 #include "menu.e"
34 #include "msg.e"
35 #include "raster.e"
36 #include "rect.e"
37 #include "ruler.e"
38 #include "select.e"
39 #include "setup.e"
40 #include "strtbl.e"
41 #include "text.e"
42 #include "util.e"
43 #include "xpixmap.e"
44 
45 #define INCH_H (RULER_W-2)
46 
47 #define HALF_INCH_H 10
48 #define QUARTER_INCH_H 6
49 #define MIN_INCH_H 3
50 
51 #define HALF_CM_H 8
52 #define MIN_CM_H 4
53 
54 int showMeasurement=FALSE;
55 int showMeasurementInTooltip=TRUE;
56 int simpleMotionInDrawWin=FALSE;
57 int showCrossHair=FALSE;
58 
59 static int measureTooltipVerbose=FALSE;
60 
61 static float gfPixelsPerUnit=(float)1.0;
62 static float gfNumUnits=(float)1.0;
63 static float gfNumFracUnits=(float)1.0;
64 
65 static char numUnitStr[80], baseUnitStr[80], unitStr[80], formatUnitStr[80];
66 
67 static GC rulerGC;
68 
69 static int oldXOff=(-1), oldYOff=(-1);
70 static int rulerLen=0;
71 static int hRulerJustRedrawn=TRUE, justUnFrozen=FALSE;
72 static int freezeMarkRulerText=FALSE, frozenXOff=0, frozenYOff=0;
73 static char *gpszFrozenDeltaStr=NULL;
74 
75 static char *gpszOldDeltaStr=NULL;
76 
GetUnitSpec(buf)77 void GetUnitSpec(buf)
78    char *buf;
79 {
80    if (*formatUnitStr == '\0') {
81       sprintf(buf, "%s %s/%s",
82             (*numUnitStr)=='\0' ? "1" : numUnitStr,
83             (*baseUnitStr)=='\0' ? "pixel" : baseUnitStr,
84             (*unitStr)=='\0' ? "pixel" : unitStr);
85    } else {
86       char buf1[80];
87 
88       FormatFloat(&gfNumFracUnits, buf1);
89       sprintf(buf, "%s %s/%s;%s;%s",
90             (*numUnitStr)=='\0' ? "1" : numUnitStr,
91             (*baseUnitStr)=='\0' ? "pixel" : baseUnitStr,
92             (*unitStr)=='\0' ? "pixel" : unitStr, formatUnitStr, buf1);
93    }
94 }
95 
ShowUnitMsg()96 void ShowUnitMsg()
97 {
98    sprintf(gszMsgBox, TgLoadString(STID_MEASUREMENT_SHOWN_IN_UNIT),
99          (*unitStr)=='\0' ? "pixel" : unitStr,
100          (*numUnitStr)=='\0' ? "1" : numUnitStr,
101          (*baseUnitStr)=='\0' ? "pixel" : baseUnitStr);
102    Msg(gszMsgBox);
103 }
104 
105 static
BadUnit(spec)106 int BadUnit(spec)
107    char *spec;
108 {
109    if (msgWindow != None) {
110       sprintf(gszMsgBox, TgLoadString(STID_BAD_MEASUREMENT_UNIT_SPEC), spec);
111       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
112    }
113    return FALSE;
114 }
115 
116 static
VerifyFormatUnitStr(format_unit_str)117 int VerifyFormatUnitStr(format_unit_str)
118    char *format_unit_str;
119 {
120    char *c_ptr=format_unit_str, *semi_ptr=NULL;
121    int seek_percent=TRUE, count=0;
122 
123    if ((semi_ptr=strchr(format_unit_str, ';')) == NULL) {
124       return FALSE;
125    } else {
126       float fval;
127 
128       *semi_ptr++ = '\0';
129       UtilTrimBlanks(semi_ptr);
130       if (sscanf(semi_ptr, "%f", &fval) != 1) {
131          return FALSE;
132       }
133       gfNumFracUnits = fval;
134    }
135    for (c_ptr=format_unit_str; *c_ptr != '\0'; c_ptr++) {
136       if (seek_percent) {
137          if (*c_ptr == '%') {
138             seek_percent = FALSE;
139          }
140       } else if (*c_ptr == 'f') {
141          seek_percent = TRUE;
142          count++;
143       } else if (!(*c_ptr == '.' || (*c_ptr >= '0' && *c_ptr <= '9'))) {
144          return FALSE;
145       }
146    }
147    if (seek_percent && count==2) {
148       strcpy(formatUnitStr, format_unit_str);
149       return TRUE;
150    }
151    return FALSE;
152 }
153 
SetUnit(spec)154 int SetUnit(spec)
155    char *spec;
156 {
157    char *spec_copy=UtilStrDup(spec);
158    char *num_ptr=NULL, *base_ptr=NULL, *unit_ptr=NULL, *semi_ptr=NULL;
159    int ok=TRUE;
160 
161    if (spec_copy == NULL) {
162       return FALSE;
163    }
164    if ((semi_ptr=strchr(spec_copy, ';')) != NULL) {
165       *semi_ptr++ = '\0';
166       if (!VerifyFormatUnitStr(semi_ptr)) {
167          ok = BadUnit(spec);
168       }
169    } else {
170       *formatUnitStr = '\0';
171    }
172    if ((num_ptr=strtok(spec_copy, " \t\n\r")) != NULL &&
173          (base_ptr=strtok(NULL, "/ \t\n\r")) != NULL &&
174          (unit_ptr=strtok(NULL, "/ \t\n\r")) != NULL) {
175       float fval;
176 
177       if (sscanf(num_ptr, "%f", &fval) == 1 && fval > INT_TOL &&
178             *base_ptr != '\0' && *unit_ptr != '\0') {
179          gfNumUnits = fval;
180          strcpy(numUnitStr, num_ptr);
181          if (UtilStrICmp("pixel", unit_ptr) == 0) {
182             *unitStr = '\0';
183          } else {
184             strcpy(unitStr, unit_ptr);
185          }
186          switch (*base_ptr) {
187          case 'i':
188          case 'I':
189             gfPixelsPerUnit = gfNumUnits * ((float)ONE_INCH);
190             strcpy(baseUnitStr, "in");
191             break;
192          case 'c':
193          case 'C':
194             gfPixelsPerUnit = gfNumUnits * ((float)ONE_CM);
195             strcpy(baseUnitStr, "cm");
196             break;
197          case 'p':
198          case 'P':
199             gfPixelsPerUnit = gfNumUnits;
200             *baseUnitStr = '\0';
201             break;
202          default: ok = BadUnit(spec); break;
203          }
204       } else {
205          ok = BadUnit(spec);
206       }
207    } else {
208       ok = BadUnit(spec);
209    }
210    if (!ok) {
211       gfPixelsPerUnit = gfNumUnits = (float)1.0;
212       *numUnitStr = *baseUnitStr = *unitStr = *formatUnitStr = '\0';
213    }
214    free(spec_copy);
215    return ok;
216 }
217 
InitRuler()218 void InitRuler()
219 {
220    XGCValues values;
221    char *c_ptr;
222 
223    values.foreground = myFgPixel;
224    values.background = (threeDLook ? myLtGryPixel : myBgPixel);
225    values.fill_style = FillSolid;
226    values.font = rulerFontPtr->fid;
227 
228    rulerGC = XCreateGC(mainDisplay, mainWindow,
229          GCForeground | GCBackground | GCFillStyle | GCFont, &values);
230 
231    showMeasurement = FALSE;
232    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME, "ShowMeasurement")) != NULL &&
233          UtilStrICmp(c_ptr, "true") == 0) {
234       showMeasurement = TRUE;
235    }
236    showCrossHair = FALSE;
237 /* if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME, "ShowCrossHair")) != NULL &&
238          UtilStrICmp(c_ptr, "true") == 0) {
239       showCrossHair = TRUE;
240    } */
241 
242    gfPixelsPerUnit = gfNumUnits = (float)1.0;
243    *numUnitStr = *baseUnitStr = *unitStr = *formatUnitStr = '\0';
244    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME, "ShowMeasurementUnit")) !=
245          NULL) {
246       char spec[80];
247 
248       if (strcmp("pixel", c_ptr) == 0) {
249          strcpy(spec, "1 pixel/pixel");
250       } else if (strcmp("inch", c_ptr) == 0) {
251          sprintf(spec, "%1d pixel/in", ONE_INCH);
252       } else if (strcmp("cm", c_ptr) == 0) {
253          sprintf(spec, "%1d pixel/cm", ONE_CM);
254       } else {
255          strcpy(spec, c_ptr);
256       }
257       if (!SetUnit(spec)) {
258          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
259                TOOL_NAME, "ShowMeasurementUnit", spec, "pixel");
260          fprintf(stderr, "\n");
261       }
262    }
263    rulerLen = rulerW-1;
264 }
265 
CleanUpRuler()266 void CleanUpRuler()
267 {
268    XFreeGC(mainDisplay, rulerGC);
269 }
270 
PixelToMeasurementUnit(Buf,NumPixels)271 void PixelToMeasurementUnit(Buf, NumPixels)
272    char *Buf;
273    int NumPixels;
274 {
275    float fval;
276    int ival;
277 
278    if (*unitStr == '\0') {
279       sprintf(Buf, "%+1d", NumPixels);
280    } else {
281       fval = (float)((float)NumPixels*1000.0)/(gfPixelsPerUnit);
282       ival = ((int)round(fval));
283       fval = ((float)ival)/((float)1000.0);
284       if (*formatUnitStr == '\0') {
285          sprintf(Buf, "%+.3f%s", fval, unitStr);
286       } else {
287          float frac;
288 
289          ival = (int)fval;
290          frac = ((ival >= 0) ? fval-((float)ival) : ((float)ival)-fval);
291          fval = (float)ival;
292          sprintf(Buf, formatUnitStr, fval, frac*gfNumFracUnits);
293       }
294    }
295 }
296 
SquarePixelToMeasurementUnit(Buf,NumSquarePixels)297 void SquarePixelToMeasurementUnit(Buf, NumSquarePixels)
298    char *Buf;
299    int NumSquarePixels;
300 {
301    float fval;
302    int ival;
303 
304    if (*unitStr == '\0') {
305       sprintf(Buf, "%+1d", NumSquarePixels);
306    } else {
307       fval = (float)((float)NumSquarePixels*1000.0)/(gfPixelsPerUnit);
308       fval = (float)(fval/gfPixelsPerUnit);
309       ival = ((int)round(fval));
310       fval = ((float)ival)/((float)1000.0);
311       if (*formatUnitStr == '\0') {
312          sprintf(Buf, "%+.3f sq %s", fval, unitStr);
313       } else {
314          float frac;
315 
316          ival = (int)fval;
317          frac = ((ival >= 0) ? fval-((float)ival) : ((float)ival)-fval);
318          fval = (float)ival;
319          sprintf(Buf, formatUnitStr, fval, frac*gfNumFracUnits);
320       }
321    }
322 }
323 
PixelToCurrentUnit(Buf,NumPixels)324 void PixelToCurrentUnit(Buf, NumPixels)
325    char *Buf;
326    int NumPixels;
327 {
328    int unit_str_was_empty=(*unitStr == '\0');
329    char num_unit_str[80], base_unit_str[80];
330    float pixel_per_unit=gfPixelsPerUnit, num_units=gfNumUnits;
331 
332    if (unit_str_was_empty) {
333       UtilStrCpyN(num_unit_str, sizeof(num_unit_str), numUnitStr);
334       UtilStrCpyN(base_unit_str, sizeof(base_unit_str), baseUnitStr);
335       switch (gridSystem) {
336       case ENGLISH_GRID:
337          gfPixelsPerUnit = 128;
338          gfNumUnits = 1;
339          UtilStrCpyN(unitStr, sizeof(unitStr), "inch");
340          break;
341       case METRIC_GRID:
342          gfPixelsPerUnit = 50;
343          gfNumUnits = 2.5;
344          UtilStrCpyN(unitStr, sizeof(unitStr), "cm");
345          break;
346       }
347       PixelToMeasurementUnit(Buf, NumPixels);
348    } else {
349       PixelToMeasurementUnit(Buf, NumPixels);
350    }
351    if (unit_str_was_empty) {
352       gfPixelsPerUnit = pixel_per_unit;
353       gfNumUnits = num_units;
354       *unitStr = '\0';
355       UtilStrCpyN(numUnitStr, sizeof(numUnitStr), num_unit_str);
356       UtilStrCpyN(baseUnitStr, sizeof(baseUnitStr), base_unit_str);
357    }
358 }
359 
360 static
RightMarginMarkVisible()361 int RightMarginMarkVisible()
362 {
363    return ((rightMarginEnabled == TRUE) && (drawOrigX+drawWinW >
364          paperWidth-(rightMargin>>TIK_PER_PIXEL_SHIFTS)));
365 }
366 
367 static
GetRightMarginMarkVs(vs)368 void GetRightMarginMarkVs(vs)
369    XPoint vs[6];
370 {
371    int x=OFFSET_X(paperWidth-(rightMargin>>TIK_PER_PIXEL_SHIFTS))-1;
372    int delta=(rulerW>>2);
373    int y_end=rulerW-(threeDLook ? ((windowPadding>>1)+2) : 1);
374    int y_start=(threeDLook ? ((windowPadding>>1)-2) : (-1))+(delta<<1);
375 
376    vs[0].x = x;        vs[0].y = y_start;
377    vs[1].x = x-delta;  vs[1].y = y_start+delta;
378    vs[2].x = x-delta;  vs[2].y = y_end;
379    vs[3].x = x+delta;  vs[3].y = y_end;
380    vs[4].x = x+delta;  vs[4].y = y_start+delta;
381    vs[5].x = x;        vs[5].y = y_start;
382 }
383 
384 static
PtInRightMarginMark(button_x,button_y)385 int PtInRightMarginMark(button_x, button_y)
386    int button_x, button_y;
387 {
388    XPoint vs[6];
389 
390    GetRightMarginMarkVs(vs);
391 
392    return PointInPolygon(button_x, button_y, 6, vs);
393 }
394 
395 static
RedrawRightMargin(dpy,win)396 void RedrawRightMargin(dpy, win)
397    Display *dpy;
398    Window win;
399 {
400    if (rightMarginEnabled != TRUE) return;
401 
402    if (drawOrigX+drawWinW > paperWidth-(rightMargin>>TIK_PER_PIXEL_SHIFTS)) {
403       int delta=(rulerW>>2);
404       int x=OFFSET_X(paperWidth-(rightMargin>>TIK_PER_PIXEL_SHIFTS))-1;
405       int y_end=rulerW-(threeDLook ? ((windowPadding>>1)+2) : 1);
406       int y_start=(threeDLook ? ((windowPadding>>1)-2) : (-1))+(delta<<1);
407       int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel);
408       GC gc=defaultGC;
409       XPoint vs[6];
410 
411       GetRightMarginMarkVs(vs);
412       if (threeDLook) {
413          XSetForeground(mainDisplay, textMenuGC, bg_pixel);
414          XFillPolygon(dpy, win, textMenuGC, vs, 6, Convex, CoordModeOrigin);
415          XDrawLines(dpy, win, gc, vs, 6, CoordModeOrigin);
416       } else {
417          XDrawLines(dpy, win, gc, vs, 6, CoordModeOrigin);
418       }
419       rightMarginActive = ShouldRightMarginBeActive();
420       if (!rightMarginActive) {
421          XDrawLine(dpy, win, gc, x-delta, y_start+1, x+delta-1, y_end);
422          XDrawLine(dpy, win, gc, x-delta+1, y_start+1, x+delta, y_end);
423          /*
424           * The code doesn't look right, but it look symmetrical on the screen!
425           *
426           * XDrawLine(dpy, win, gc, x+delta, y_start+1, x-delta+1, y_end);
427           * XDrawLine(dpy, win, gc, x+delta-1, y_start+1, x-delta, y_end);
428           */
429          XDrawLine(dpy, win, gc, x+delta+1, y_start, x-delta+1, y_end);
430          XDrawLine(dpy, win, gc, x+delta, y_start, x-delta, y_end);
431       }
432    }
433 }
434 
RedrawHRuler(dpy,win)435 void RedrawHRuler(dpy, win)
436    Display *dpy;
437    Window win;
438 {
439    register int i, pos, len, index;
440    int inc, abs_inc, y=rulerW-(threeDLook ? (windowPadding>>1) : 0);
441    char s[5];
442 
443    switch (gridSystem) {
444    case ENGLISH_GRID:
445       inc = (zoomedIn ? (xyEnglishGrid<<zoomScale) : xyEnglishGrid);
446       abs_inc = ABS_SIZE(inc);
447 
448       if (drawOrigX % abs_inc == 0) {
449          i = 0;
450          pos = 0;
451       } else {
452          i = ((int)(drawOrigX / abs_inc) + 1) * abs_inc - drawOrigX;
453          pos = ZOOMED_SIZE(i);
454       }
455       pos--; /* since drawWindow has no border */
456       for ( ; i<drawWinW; i+=abs_inc, pos+=inc) {
457          if ((GRID_ZOOMED_SIZE(i+drawOrigX)) % ONE_INCH == 0) {
458             index = (int)((GRID_ZOOMED_SIZE(i+drawOrigX)) / ONE_INCH);
459             sprintf(s, "%1d", GRID_ABS_SIZE(index));
460             len = strlen(s);
461             XDrawString(dpy, win, rulerGC, pos+2, y-INCH_H+rulerFontAsc,
462                   s, len);
463             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-INCH_H);
464          } else if ((GRID_ZOOMED_SIZE(i+drawOrigX)) % HALF_INCH == 0) {
465             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-HALF_INCH_H);
466          } else if ((GRID_ZOOMED_SIZE(i+drawOrigX)) % QUARTER_INCH == 0) {
467             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-QUARTER_INCH_H);
468          } else {
469             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-MIN_INCH_H);
470          }
471       }
472       break;
473    case METRIC_GRID:
474       inc = (zoomedIn ? (xyMetricGrid<<zoomScale) : xyMetricGrid);
475       abs_inc = ABS_SIZE(inc);
476 
477       if (drawOrigX % abs_inc == 0) {
478          i = 0;
479          pos = 0;
480       } else {
481          i = ((int)(drawOrigX / abs_inc) + 1) * abs_inc - drawOrigX;
482          pos = ZOOMED_SIZE(i);
483       }
484       pos--; /* since drawWindow has no border */
485       for ( ; i<drawWinW; i+=abs_inc, pos+=inc) {
486          if ((GRID_ZOOMED_SIZE(i+drawOrigX)) % ONE_CM == 0) {
487             index = (int)((GRID_ZOOMED_SIZE(i+drawOrigX)) / ONE_CM);
488             sprintf(s, "%1d", GRID_ABS_SIZE(index));
489             len = strlen(s);
490             XDrawString(dpy, win, rulerGC, pos+2, y-INCH_H+rulerFontAsc,
491                   s, len);
492             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-INCH_H);
493          } else if ((GRID_ZOOMED_SIZE(i+drawOrigX)) % FIVE_MM == 0) {
494             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-HALF_CM_H);
495          } else {
496             XDrawLine(dpy, win, defaultGC, pos, y, pos, y-MIN_CM_H);
497          }
498       }
499       break;
500    }
501    RedrawRightMargin(dpy, win);
502 }
503 
504 static
DrawHRuleTick(XOff)505 void DrawHRuleTick(XOff)
506    int XOff;
507 {
508    XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, XOff, 0, XOff, rulerLen);
509 }
510 
511 static
DrawVRuleTick(YOff)512 void DrawVRuleTick(YOff)
513 {
514    XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, YOff, rulerLen, YOff);
515 }
516 
RedrawHRulerWindow()517 void RedrawHRulerWindow()
518 {
519    XEvent ev;
520 
521    XClearWindow(mainDisplay, hRuleWindow);
522 
523    XSync(mainDisplay, False);
524    while (XCheckWindowEvent(mainDisplay, hRuleWindow, ExposureMask, &ev)) ;
525 
526    RedrawHRuler(mainDisplay, hRuleWindow);
527 
528    oldXOff = (-1);
529    DrawHRuleTick(oldXOff-1);
530    hRulerJustRedrawn = TRUE;
531    justUnFrozen = FALSE;
532 }
533 
RedrawVRuler(dpy,win)534 void RedrawVRuler(dpy, win)
535    Display *dpy;
536    Window win;
537 {
538    register int i, pos, len, index;
539    int inc, abs_inc, x=rulerW-(threeDLook ? (windowPadding>>1) : 0);
540    char s[5];
541 
542    switch (gridSystem) {
543    case ENGLISH_GRID:
544       inc = (zoomedIn ? (xyEnglishGrid<<zoomScale) : xyEnglishGrid);
545       abs_inc = ABS_SIZE(inc);
546 
547       if (drawOrigY % abs_inc == 0) {
548          i = 0;
549          pos = 0;
550       } else {
551          i = ((int)(drawOrigY / abs_inc) + 1) * abs_inc - drawOrigY;
552          pos = ZOOMED_SIZE(i);
553       }
554       pos--; /* since drawWindow has no border */
555       for ( ; i<drawWinH; i+=abs_inc, pos+=inc) {
556          if ((GRID_ZOOMED_SIZE(i+drawOrigY)) % ONE_INCH == 0) {
557             index = (int)((GRID_ZOOMED_SIZE(i+drawOrigY)) / ONE_INCH);
558             sprintf(s, "%1d", GRID_ABS_SIZE(index));
559             len = strlen(s);
560             XDrawString(dpy, win, rulerGC, 1, pos+rulerFontAsc+2, s, len);
561             XDrawLine(dpy, win, defaultGC, x, pos, x-INCH_H, pos);
562          } else if ((GRID_ZOOMED_SIZE(i+drawOrigY)) % HALF_INCH == 0) {
563             XDrawLine(dpy, win, defaultGC, x, pos, x-HALF_INCH_H, pos);
564          } else if ((GRID_ZOOMED_SIZE(i+drawOrigY)) % QUARTER_INCH == 0) {
565             XDrawLine(dpy, win, defaultGC, x, pos, x-QUARTER_INCH_H, pos);
566          } else {
567             XDrawLine(dpy, win, defaultGC, x, pos, x-MIN_INCH_H, pos);
568          }
569       }
570       break;
571    case METRIC_GRID:
572       inc = (zoomedIn ? (xyMetricGrid<<zoomScale) : xyMetricGrid);
573       abs_inc = ABS_SIZE(inc);
574 
575       if (drawOrigY % abs_inc == 0) {
576          i = 0;
577          pos = 0;
578       } else {
579          i = ((int)(drawOrigY / abs_inc) + 1) * abs_inc - drawOrigY;
580          pos = ZOOMED_SIZE(i);
581       }
582       pos--; /* since drawWindow has no border */
583       for ( ; i<drawWinH; i+=abs_inc, pos+=inc) {
584          if ((GRID_ZOOMED_SIZE(i+drawOrigY)) % ONE_CM == 0) {
585             index = (int)((GRID_ZOOMED_SIZE(i+drawOrigY)) / ONE_CM);
586             sprintf(s, "%1d", GRID_ABS_SIZE(index));
587             len = strlen(s);
588             XDrawString(dpy, win, rulerGC, 1, pos+rulerFontAsc+2, s, len);
589             XDrawLine(dpy, win, defaultGC, x, pos, x-INCH_H, pos);
590          } else if ((GRID_ZOOMED_SIZE(i+drawOrigY)) % FIVE_MM == 0) {
591             XDrawLine(dpy, win, defaultGC, x, pos, x-HALF_CM_H, pos);
592          } else {
593             XDrawLine(dpy, win, defaultGC, x, pos, x-MIN_CM_H, pos);
594          }
595       }
596       break;
597    }
598 }
599 
RedrawVRulerWindow()600 void RedrawVRulerWindow()
601 {
602    XEvent ev;
603 
604    XClearWindow(mainDisplay, vRuleWindow);
605 
606    XSync(mainDisplay, False);
607    while (XCheckWindowEvent(mainDisplay, vRuleWindow, ExposureMask, &ev)) ;
608 
609    RedrawVRuler(mainDisplay, vRuleWindow);
610 
611    if (!freezeMarkRulerText && showMeasurement && !hRulerJustRedrawn &&
612          oldYOff != 0) {
613       char x_buf[80], y_buf[80], buf[80];
614 
615       PixelToMeasurementUnit(x_buf, ABS_X(oldXOff));
616       PixelToMeasurementUnit(y_buf, ABS_Y(oldYOff));
617       sprintf(buf, "[%s,%s]", x_buf, y_buf);
618       if (!showMeasurementInTooltip) {
619          XDrawString(mainDisplay, hRuleWindow, revDefaultGC,
620                8, 2+defaultFontAsc, buf, strlen(buf));
621       }
622       hRulerJustRedrawn = TRUE;
623    }
624    oldYOff = (-1);
625    DrawVRuleTick(oldYOff-1);
626 }
627 
MarkRulers(XOff,YOff)628 void MarkRulers(XOff, YOff)
629    int XOff, YOff;
630 {
631    char x_buf[80], y_buf[80], buf[MAXSTRING];
632 
633    DrawHRuleTick(oldXOff-1);
634    DrawVRuleTick(oldYOff-1);
635    if (showCrossHair) {
636       XDrawLine(mainDisplay, drawWindow, revDefaultGC, oldXOff, 0,
637             oldXOff, ZOOMED_SIZE(drawWinH));
638       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, oldYOff,
639             ZOOMED_SIZE(drawWinW), oldYOff);
640    }
641    if (hRulerJustRedrawn) {
642       hRulerJustRedrawn= FALSE;
643    } else if (!freezeMarkRulerText && showMeasurement) {
644       if (justUnFrozen) {
645          justUnFrozen = FALSE;
646          PixelToMeasurementUnit(x_buf, ABS_X(frozenXOff));
647          PixelToMeasurementUnit(y_buf, ABS_Y(frozenYOff));
648          sprintf(buf, "[%s,%s]", x_buf, y_buf);
649       } else {
650          PixelToMeasurementUnit(x_buf, ABS_X(oldXOff));
651          PixelToMeasurementUnit(y_buf, ABS_Y(oldYOff));
652          sprintf(buf, "[%s,%s]", x_buf, y_buf);
653       }
654       if (!showMeasurementInTooltip) {
655          XDrawString(mainDisplay, hRuleWindow, revDefaultGC,
656                8, 2+defaultFontAsc, buf, strlen(buf));
657       }
658    }
659    DrawHRuleTick(XOff-1);
660    DrawVRuleTick(YOff-1);
661 
662    if (showCrossHair) {
663       XDrawLine(mainDisplay, drawWindow, revDefaultGC, XOff, 0,
664             XOff, ZOOMED_SIZE(drawWinH));
665       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, YOff,
666             ZOOMED_SIZE(drawWinW), YOff);
667    }
668    if (!freezeMarkRulerText && showMeasurement) {
669       PixelToMeasurementUnit(x_buf, ABS_X(XOff));
670       PixelToMeasurementUnit(y_buf, ABS_Y(YOff));
671       if (simpleMotionInDrawWin && curChoice == NOTHING && topSel != NULL &&
672             VerboseMeasureTooltip()) {
673          char w_buf[80], h_buf[80];
674          char lx_buf[80], ty_buf[80], rx_buf[80], by_buf[80];
675 
676          PixelToMeasurementUnit(w_buf, selObjRbX-selObjLtX);
677          PixelToMeasurementUnit(h_buf, selObjRbY-selObjLtY);
678          PixelToMeasurementUnit(lx_buf, selObjLtX);
679          PixelToMeasurementUnit(rx_buf, selObjRbX);
680          PixelToMeasurementUnit(ty_buf, selObjLtY);
681          PixelToMeasurementUnit(by_buf, selObjRbY);
682          sprintf(buf,
683          "[%s,%s]\nsel_lx=%s sel_rx=%s\nsel_ty=%s sel_by=%s\nsel_w=%s sel_h=%s",
684                x_buf, y_buf, lx_buf, rx_buf, ty_buf, by_buf, w_buf, h_buf);
685       } else {
686          sprintf(buf, "[%s,%s]", x_buf, y_buf);
687       }
688       if (showMeasurementInTooltip) {
689          SetMeasureTooltip(buf);
690       } else {
691          XDrawString(mainDisplay, hRuleWindow, revDefaultGC,
692                8, 2+defaultFontAsc, buf, strlen(buf));
693       }
694    } else if (VerboseMeasureTooltip()) {
695       switch (curChoice) {
696       case NOTHING:
697       case DRAWPOLY:
698       case DRAWPOLYGON:
699       case VERTEXMODE:
700          DoIntervalMeasureTooltip(frozenXOff, frozenYOff, XOff, YOff,
701                (frozenXOff+XOff)>>1, (frozenYOff+YOff)>>1, gpszFrozenDeltaStr);
702          break;
703       }
704    }
705    oldXOff = XOff;
706    oldYOff = YOff;
707 }
708 
RedrawRulers()709 void RedrawRulers()
710 {
711    RedrawHRulerWindow();
712    RedrawVRulerWindow();
713 }
714 
GetCrossHairPosition(pnXOff,pnYOff,pnShown)715 void GetCrossHairPosition(pnXOff, pnYOff, pnShown)
716    int *pnXOff, *pnYOff, *pnShown;
717 {
718    if (pnXOff != NULL) *pnXOff = oldXOff;
719    if (pnYOff != NULL) *pnYOff = oldYOff;
720    if (pnShown != NULL) *pnShown = showCrossHair;
721 }
722 
RedrawCrossHair()723 void RedrawCrossHair()
724 {
725    if (showCrossHair) {
726       XDrawLine(mainDisplay, drawWindow, revDefaultGC, oldXOff, 0,
727             oldXOff, ZOOMED_SIZE(drawWinH));
728       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, oldYOff,
729             ZOOMED_SIZE(drawWinW), oldYOff);
730    }
731 }
732 
ToggleShowCrossHair()733 void ToggleShowCrossHair()
734 {
735    /* the cross-hair stuff is disabled -- cannot get it to work right */
736    if (!showCrossHair) return;
737 
738    RedrawCrossHair();
739 
740 /* showCrossHair = !showCrossHair; */
741    showCrossHair = FALSE;
742    RedrawRulers();
743    if (showCrossHair) {
744       Msg(TgLoadString(STID_SHOW_CROSSHAIR_ENABLED));
745       SetNullCursor(drawWindow);
746    } else {
747       SetDefaultCursor(drawWindow);
748       ShowCursor();
749       Msg(TgLoadString(STID_SHOW_CROSSHAIR_DISABLED));
750    }
751 }
752 
753 static
ContinueMoveRightMarginMark(dpy,OrigX,OrigY)754 void ContinueMoveRightMarginMark(dpy, OrigX, OrigY)
755    Display *dpy;
756    int OrigX, OrigY; /* these are mouse coordinates */
757 {
758    int moving=TRUE, dx=0, grid_x=0, grid_y=0, h=ZOOMED_SIZE(drawWinH);
759    int prev_x=0, orig_grid_x=0, orig_grid_y=0, orig_right_margin=0;
760    char buf[80], buf2[80];
761    double dval=(double)0;
762    XPoint orig_vs[6], vs[6];
763    XEvent ev;
764 
765    XFlush(dpy);
766    XSync(dpy, False);
767 
768    if (XCheckMaskEvent(dpy, ExposureMask, &ev) ||
769          XCheckMaskEvent(dpy, VisibilityChangeMask, &ev)) {
770       ExposeEventHandler(&ev, TRUE);
771    }
772    GridXY(OrigX, OrigY, &grid_x, &grid_y);
773    orig_grid_x = grid_x;
774    orig_grid_y = grid_y;
775    orig_right_margin = OFFSET_X(paperWidth-(rightMargin>>TIK_PER_PIXEL_SHIFTS));
776    prev_x = orig_right_margin+0;
777    GetRightMarginMarkVs(orig_vs);
778    memcpy(vs, orig_vs, 6*sizeof(XPoint));
779    dval = ((double)rightMargin) * ((double)printMag) / ((double)TIK_PER_PIXEL);
780    dval /= ((double)100);
781    PixelToMeasurementUnit(buf, round(dval));
782    sprintf(buf2, "margin=%s", buf);
783    StartShowMeasureCursor(OrigX, 4, buf2, FALSE);
784 
785    /* draw */
786    XDrawLine(dpy, drawWindow, revDefaultGC, prev_x, 0, prev_x, h);
787    XDrawLines(dpy, hRuleWindow, revDefaultGC, vs, 6, CoordModeOrigin);
788    if (!debugNoPointerGrab) {
789       XGrabPointer(dpy, drawWindow, FALSE,
790             PointerMotionMask | ButtonReleaseMask,
791             GrabModeAsync, GrabModeAsync, None, moveCursor, CurrentTime);
792    }
793    while (moving) {
794       XEvent input;
795 
796       XNextEvent(dpy, &input);
797 
798       if (input.type == Expose || input.type == VisibilityNotify) {
799          ExposeEventHandler(&input, TRUE);
800       } else if (input.type == ButtonRelease) {
801          int abs_dx=ABS_SIZE(dx);
802 
803          XUngrabPointer(dpy, CurrentTime);
804          XSync(dpy, False);
805 
806          moving = FALSE;
807 
808          /* erase */
809          XDrawLine(dpy, drawWindow, revDefaultGC, prev_x, 0, prev_x, h);
810          XDrawLines(dpy, hRuleWindow, revDefaultGC, vs, 6, CoordModeOrigin);
811          EndShowMeasureCursor(OrigX+dx, 4, buf2, FALSE);
812 
813          rightMargin -= (abs_dx<<TIK_PER_PIXEL_SHIFTS);
814          RedrawHRulerWindow();
815       } else if (input.type == MotionNotify) {
816          int i=0, mouse_x=0, mouse_y=0;
817 
818          /* erase */
819          XDrawLine(dpy, drawWindow, revDefaultGC, prev_x, 0, prev_x, h);
820          XDrawLines(dpy, hRuleWindow, revDefaultGC, vs, 6, CoordModeOrigin);
821          ShowMeasureCursor(OrigX+dx, 4, buf2, FALSE);
822 
823          mouse_x = input.xmotion.x;
824          mouse_y = input.xmotion.y;
825          GridXY(mouse_x, mouse_y, &grid_x, &grid_y);
826 
827          dx = grid_x - orig_grid_x;
828          prev_x = orig_right_margin+0+dx;
829          for (i=0; i < 6; i++) {
830             vs[i].x = orig_vs[i].x+dx;
831          }
832          /* draw */
833          XDrawLine(dpy, drawWindow, revDefaultGC, prev_x, 0, prev_x, h);
834          XDrawLines(dpy, hRuleWindow, revDefaultGC, vs, 6, CoordModeOrigin);
835 
836          dval = (((double)(rightMargin-(ABS_SIZE(dx)<<TIK_PER_PIXEL_SHIFTS))) *
837                ((double)printMag) / ((double)TIK_PER_PIXEL)) / ((double)100);
838          PixelToMeasurementUnit(buf, round(dval));
839          sprintf(buf2, "margin=%s", buf);
840          ShowMeasureCursor(OrigX+dx, 4, buf2, FALSE);
841       }
842    }
843 }
844 
RulersEventHandler(input)845 void RulersEventHandler(input)
846    XEvent *input;
847 {
848    if (input->type == ButtonPress) {
849       XButtonEvent *button_ev=(&input->xbutton);
850       int mouse_x=button_ev->x, mouse_y=button_ev->y;
851 
852       if (RightMarginMarkVisible() && PtInRightMarginMark(mouse_x, mouse_y)) {
853          ContinueMoveRightMarginMark(mainDisplay, mouse_x, mouse_y);
854       } else if (button_ev->button == Button1) {
855          IncGrid();
856       } else if (button_ev->button == Button2) {
857          GridMenu(button_ev->x_root, button_ev->y_root, FALSE);
858       } else if (button_ev->button == Button3) {
859          DecGrid();
860       }
861    } else if (input->xany.window == vRuleWindow) {
862       if (input->type == Expose) {
863          RedrawVRulerWindow();
864       } else if (input->type == EnterNotify) {
865          SetMouseStatus(TgLoadCachedString(CSTID_INC_GRID_SIZE),
866                TgLoadCachedString(CSTID_GRID_MENU),
867                TgLoadCachedString(CSTID_DEC_GRID_SIZE));
868       }
869    } else if (input->xany.window == hRuleWindow) {
870       if (input->type == Expose) {
871          RedrawHRulerWindow();
872       } else if (input->type == EnterNotify) {
873          SetMouseStatus(TgLoadCachedString(CSTID_INC_GRID_SIZE),
874                TgLoadCachedString(CSTID_GRID_MENU),
875                TgLoadCachedString(CSTID_DEC_GRID_SIZE));
876       }
877    }
878 }
879 
FreezeMarkRulerText()880 void FreezeMarkRulerText()
881 {
882    if (!freezeMarkRulerText) {
883       EndMeasureTooltip(FALSE);
884    }
885    freezeMarkRulerText = TRUE;
886    frozenXOff = oldXOff;
887    frozenYOff = oldYOff;
888    UtilFree(gpszFrozenDeltaStr);
889    if (gpszOldDeltaStr != NULL) {
890       gpszFrozenDeltaStr = UtilStrDup(gpszOldDeltaStr);
891       if (gpszFrozenDeltaStr == NULL) FailAllocMessage();
892    }
893 }
894 
UnFreezeMarkRulerText()895 void UnFreezeMarkRulerText()
896 {
897    freezeMarkRulerText = FALSE;
898    justUnFrozen = TRUE;
899 }
900 
901 static int oldLtX, oldLtY, oldRbX, oldRbY, oldMdX, oldMdY;
902 
VerboseMeasureTooltip()903 int VerboseMeasureTooltip()
904 {
905    return (showMeasurement && showMeasurementInTooltip &&
906          measureTooltipVerbose);
907 }
908 
909 static
DoIntervalRulers()910 void DoIntervalRulers()
911 {
912    if (oldLtX == oldRbX) {
913       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldLtX-1, 0, oldLtX-1,
914             rulerLen);
915    } else if (oldLtX==oldMdX || oldRbX==oldMdX) {
916       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldLtX-1, 0, oldLtX-1,
917             rulerLen);
918       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldRbX-1, 0, oldRbX-1,
919             rulerLen);
920    } else {
921       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldLtX-1, 0, oldLtX-1,
922             rulerLen);
923       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldMdX-1, 0, oldMdX-1,
924             rulerLen);
925       XDrawLine(mainDisplay, hRuleWindow, revDefaultGC, oldRbX-1, 0, oldRbX-1,
926             rulerLen);
927    }
928    if (oldLtY == oldRbY) {
929       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldLtY-1, rulerLen-1,
930             oldLtY);
931    } else if (oldLtY==oldMdY || oldRbY==oldMdY) {
932       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldLtY-1, rulerLen-1,
933             oldLtY);
934       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldRbY-1, rulerLen-1,
935             oldRbY);
936    } else {
937       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldLtY-1, rulerLen,
938             oldLtY-1);
939       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldMdY-1, rulerLen,
940             oldMdY-1);
941       XDrawLine(mainDisplay, vRuleWindow, revDefaultGC, 0, oldRbY-1, rulerLen,
942             oldRbY-1);
943    }
944    if (showCrossHair) {
945       XDrawLine(mainDisplay, drawWindow, revDefaultGC, oldRbX, 0,
946             oldRbX, ZOOMED_SIZE(drawWinH));
947       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, oldRbY,
948             ZOOMED_SIZE(drawWinW), oldRbY);
949    }
950    if (VerboseMeasureTooltip()) {
951       DoIntervalMeasureTooltip(oldLtX, oldLtY, oldRbX, oldRbY, oldMdX, oldMdY,
952             gpszOldDeltaStr);
953    }
954 }
955 
BeginIntervalRulers(ltx,lty,rbx,rby)956 void BeginIntervalRulers(ltx, lty, rbx, rby)
957    int ltx, lty, rbx, rby;
958 {
959    DrawHRuleTick(oldXOff-1);
960    DrawVRuleTick(oldYOff-1);
961    if (showCrossHair) {
962       XDrawLine(mainDisplay, drawWindow, revDefaultGC, oldXOff, 0,
963             oldXOff, ZOOMED_SIZE(drawWinH));
964       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, oldYOff,
965             ZOOMED_SIZE(drawWinW), oldYOff);
966    }
967    oldLtX = ltx; oldLtY = lty;
968    oldRbX = rbx; oldRbY = rby;
969    oldMdX = (oldLtX+oldRbX)>>1;
970    oldMdY = (oldLtY+oldRbY)>>1;
971    gpszOldDeltaStr = NULL;
972    if (showMeasurement) FreezeMarkRulerText();
973    DoIntervalRulers();
974 }
975 
DrawIntervalRulers(ltx,lty,rbx,rby,delta_str)976 void DrawIntervalRulers(ltx, lty, rbx, rby, delta_str)
977    int ltx, lty, rbx, rby;
978    char *delta_str;
979 {
980    DoIntervalRulers();
981    oldLtX = ltx; oldLtY = lty;
982    oldRbX = rbx; oldRbY = rby;
983    oldMdX = (oldLtX+oldRbX)>>1; oldMdY = (oldLtY+oldRbY)>>1;
984    if (delta_str != NULL) {
985       gpszOldDeltaStr = UtilStrDup(delta_str);
986       if (gpszOldDeltaStr == NULL) FailAllocMessage();
987    }
988    DoIntervalRulers();
989    if (showMeasurement) UnFreezeMarkRulerText();
990 }
991 
EndIntervalRulers(x,y)992 void EndIntervalRulers(x, y)
993    int x, y;
994 {
995    DoIntervalRulers();
996    oldXOff = x;
997    oldYOff = y;
998    UtilFree(gpszOldDeltaStr);
999    gpszOldDeltaStr = NULL;
1000    DrawHRuleTick(oldXOff-1);
1001    DrawVRuleTick(oldYOff-1);
1002    if (showCrossHair) {
1003       XDrawLine(mainDisplay, drawWindow, revDefaultGC, oldXOff, 0,
1004             oldXOff, ZOOMED_SIZE(drawWinH));
1005       XDrawLine(mainDisplay, drawWindow, revDefaultGC, 0, oldYOff,
1006             ZOOMED_SIZE(drawWinW), oldYOff);
1007    }
1008    MarkRulers(x, y);
1009    frozenXOff = oldXOff;
1010    frozenYOff = oldYOff;
1011 }
1012 
StartShowMeasureCursor(XOff,YOff,Str,ExtraSpace)1013 void StartShowMeasureCursor(XOff, YOff, Str, ExtraSpace)
1014    int XOff, YOff, ExtraSpace;
1015    char *Str;
1016 {
1017    if (!showMeasurement) return;
1018    MarkRulers(XOff, YOff);
1019    FreezeMarkRulerText();
1020    if (Str == NULL || *Str == '\0') return;
1021 
1022    if (showMeasurementInTooltip) {
1023       if (!measureTooltipVerbose) {
1024          StartMeasureTooltip(Str);
1025       }
1026    } else {
1027       int x=(ExtraSpace ? XOff+18 : XOff+4), y=YOff+defaultFontAsc;
1028       char *c_ptr=strchr(Str, '\n'), *line=Str;
1029 
1030       while (line != NULL) {
1031          if (c_ptr != NULL) *c_ptr = '\0';
1032          XDrawString(mainDisplay, drawWindow, revDefaultGC, x, y,
1033                line, strlen(line));
1034          if (c_ptr != NULL) {
1035             *c_ptr++ = '\n';
1036             line = c_ptr;
1037             c_ptr = strchr(line, '\n');
1038             y += defaultFontHeight;
1039          } else {
1040             break;
1041          }
1042       }
1043    }
1044 }
1045 
ShowMeasureCursor(XOff,YOff,Str,ExtraSpace)1046 void ShowMeasureCursor(XOff, YOff, Str, ExtraSpace)
1047    int XOff, YOff, ExtraSpace;
1048    char *Str;
1049 {
1050    if (!showMeasurement) return;
1051    if (Str == NULL || *Str == '\0') return;
1052 
1053    if (showMeasurementInTooltip) {
1054       if (measureTooltipVerbose) {
1055          switch (curChoice) {
1056          case DRAWARC:
1057          case DRAWEDGEARC:
1058          case FREEHAND:
1059          case ROTATEMODE:
1060             SetMeasureTooltip(Str);
1061             break;
1062          }
1063       } else {
1064          SetMeasureTooltip(Str);
1065       }
1066    } else {
1067       int x=(ExtraSpace ? XOff+18 : XOff+4), y=YOff+defaultFontAsc;
1068       char *c_ptr=strchr(Str, '\n'), *line=Str;
1069 
1070       while (line != NULL) {
1071          if (c_ptr != NULL) *c_ptr = '\0';
1072          XDrawString(mainDisplay, drawWindow, revDefaultGC, x, y,
1073                line, strlen(line));
1074          if (c_ptr != NULL) {
1075             *c_ptr++ = '\n';
1076             line = c_ptr;
1077             c_ptr = strchr(line, '\n');
1078             y += defaultFontHeight;
1079          } else {
1080             break;
1081          }
1082       }
1083    }
1084 }
1085 
EndShowMeasureCursor(XOff,YOff,Str,ExtraSpace)1086 void EndShowMeasureCursor(XOff, YOff, Str, ExtraSpace)
1087    int XOff, YOff, ExtraSpace;
1088    char *Str;
1089 {
1090    if (!showMeasurement) return;
1091    if (Str == NULL || *Str == '\0') return;
1092 
1093    if (showMeasurementInTooltip) {
1094       if (!measureTooltipVerbose) {
1095          StartMeasureTooltip(Str);
1096       }
1097    } else {
1098       int x=(ExtraSpace ? XOff+18 : XOff+4), y=YOff+defaultFontAsc;
1099       char *c_ptr=strchr(Str, '\n'), *line=Str;
1100 
1101       while (line != NULL) {
1102          if (c_ptr != NULL) *c_ptr = '\0';
1103          XDrawString(mainDisplay, drawWindow, revDefaultGC, x, y,
1104                line, strlen(line));
1105          if (c_ptr != NULL) {
1106             *c_ptr++ = '\n';
1107             line = c_ptr;
1108             c_ptr = strchr(line, '\n');
1109             y += defaultFontHeight;
1110          } else {
1111             break;
1112          }
1113       }
1114    }
1115    UnFreezeMarkRulerText();
1116 }
1117 
ToggleShowMeasurement()1118 void ToggleShowMeasurement()
1119 {
1120    showMeasurement = !showMeasurement;
1121    RedrawRulers();
1122    if (showMeasurement) {
1123       Msg(TgLoadString(STID_SHOW_MEASUREMENT_ENABLED));
1124    } else {
1125       Msg(TgLoadString(STID_SHOW_MEASUREMENT_DISABLED));
1126    }
1127 }
1128 
ToggleShowMeasurementInTooltip()1129 void ToggleShowMeasurementInTooltip()
1130 {
1131    showMeasurementInTooltip = !showMeasurementInTooltip;
1132    RedrawRulers();
1133    if (showMeasurementInTooltip) {
1134       Msg(TgLoadString(STID_SHOW_MEASUREMENT_IN_TT_ENAB));
1135    } else {
1136       Msg(TgLoadString(STID_SHOW_MEASUREMENT_IN_TT_DISB));
1137    }
1138    EndMeasureTooltip(TRUE);
1139 }
1140 
1141 /* ------------------- Measure Tooltip Related Routines ------------------- */
1142 
1143 /*
1144  * Some code in the rest of this file is based on the contributed code
1145  *       by Raphael Dechenaux <raph_d@club-internet.fr>.
1146  */
1147 
1148 typedef struct tagMeasureTooltipInfo {
1149    int ltx, lty, w, h; /* this is for the window */
1150    int bbox_w, bbox_h; /* this is for the text */
1151    int x_padding, y_padding; /* padding around the text */
1152    int mapped;
1153    Window win;
1154    GC gc;
1155    /* configuration parameters -- how to display */
1156    int x_follow_mouse, y_follow_mouse, position_x, position_y;
1157 } MeasureTooltipInfo;
1158 
1159 static MeasureTooltipInfo gmti;
1160 
1161 static Pixmap tooltipBgPixmap=None;
1162 
1163 static
CalcMeasureTooltipPosition()1164 void CalcMeasureTooltipPosition()
1165 {
1166    if (gmti.x_follow_mouse || gmti.y_follow_mouse) {
1167       Window root_win=None, child_win=None;
1168       unsigned int status;
1169       int root_x=0, root_y=0;
1170 
1171       XQueryPointer(mainDisplay, rootWindow, &root_win, &child_win,
1172             &root_x, &root_y, &gmti.ltx, &gmti.lty, &status);
1173       if (gmti.x_follow_mouse) gmti.ltx += 16;
1174       if (gmti.y_follow_mouse) gmti.lty += 16;
1175       if (gmti.x_follow_mouse && gmti.y_follow_mouse) {
1176          return;
1177       }
1178    }
1179    if (!gmti.x_follow_mouse) {
1180       int dpy_w=DisplayWidth(mainDisplay,mainScreen);
1181 
1182       switch (gmti.position_x) {
1183       case ALIGN_L: gmti.ltx = 0; break;
1184       case ALIGN_C: gmti.ltx = ((dpy_w-gmti.bbox_w-gmti.x_padding)>>1); break;
1185       case ALIGN_R: gmti.ltx = (dpy_w-gmti.bbox_w-(gmti.x_padding<<1)); break;
1186       default: gmti.ltx = 0; break;
1187       }
1188    }
1189    if (!gmti.y_follow_mouse) {
1190       int dpy_h=DisplayHeight(mainDisplay,mainScreen);
1191 
1192       switch (gmti.position_y) {
1193       case ALIGN_T: gmti.lty = 0; break;
1194       case ALIGN_M: gmti.lty = ((dpy_h-gmti.bbox_h-gmti.y_padding)>>1); break;
1195       case ALIGN_B: gmti.lty = (dpy_h-gmti.bbox_h-(gmti.y_padding<<1)); break;
1196       default: gmti.lty = 0; break;
1197       }
1198    }
1199 }
1200 
1201 static
SetMeasureTooltipBBox(msg)1202 void SetMeasureTooltipBBox(msg)
1203    char *msg;
1204 {
1205    int bbox_w=0, bbox_h=0;
1206    char *psz=NULL, *line=NULL;
1207 
1208    bbox_h = ((msgFontSet==NULL && msgFontPtr==NULL) ? defaultFontHeight :
1209          msgFontHeight);
1210    line = msg;
1211    psz = strchr(line, '\n');
1212    while (line != NULL) {
1213       char saved_ch='\0';
1214       int len=0, w=0;
1215 
1216       if (psz != NULL) {
1217          saved_ch = (*psz);
1218          *psz = '\0';
1219       }
1220       len = strlen(line);
1221       if (msgFontSet == NULL && msgFontPtr == NULL) {
1222          w = (defaultFontWidth*len);
1223       } else {
1224          w = MsgTextWidth(msgFontPtr, line, len);
1225       }
1226       if (w > bbox_w) bbox_w = w;
1227 
1228       if (psz == NULL) {
1229          break;
1230       }
1231       *psz = saved_ch;
1232       line = &psz[1];
1233       psz = strchr(line, '\n');
1234 
1235       bbox_h += ((msgFontSet==NULL && msgFontPtr==NULL) ? defaultFontHeight :
1236             msgFontHeight);
1237    }
1238    gmti.bbox_w = bbox_w;
1239    gmti.bbox_h = bbox_h;
1240    gmti.w = bbox_w+((gmti.x_padding)<<1);
1241    gmti.h = bbox_h+((gmti.y_padding)<<1);
1242 
1243    CalcMeasureTooltipPosition();
1244 }
1245 
DoIntervalMeasureTooltip(ltx,lty,rbx,rby,mdx,mdy,delta_str)1246 void DoIntervalMeasureTooltip(ltx, lty, rbx, rby, mdx, mdy, delta_str)
1247    int ltx, lty, rbx, rby, mdx, mdy;
1248    char *delta_str;
1249 {
1250    char w_buf[80], h_buf[80], buf[256];
1251    char lx_buf[80], cx_buf[80], rx_buf[80];
1252    char ty_buf[80], my_buf[80], by_buf[80];
1253 
1254    PixelToMeasurementUnit(w_buf, ABS_SIZE(abs(rbx-ltx)));
1255    PixelToMeasurementUnit(h_buf, ABS_SIZE(abs(rby-lty)));
1256    PixelToMeasurementUnit(lx_buf, ABS_X(ltx < rbx ? ltx : rbx));
1257    PixelToMeasurementUnit(cx_buf, ABS_X(mdx));
1258    PixelToMeasurementUnit(rx_buf, ABS_X(ltx > rbx ? ltx : rbx));
1259    PixelToMeasurementUnit(ty_buf, ABS_Y(lty < rby ? lty : rby));
1260    PixelToMeasurementUnit(my_buf, ABS_Y(mdy));
1261    PixelToMeasurementUnit(by_buf, ABS_Y(lty > rby ? lty : rby));
1262    if (delta_str != NULL) {
1263       sprintf(buf, "lx=%s cx=%s rx=%s\nty=%s my=%s by=%s\nw=%s h=%s\n%s",
1264             lx_buf, cx_buf, rx_buf, ty_buf, my_buf, by_buf, w_buf, h_buf,
1265             delta_str);
1266    } else {
1267       sprintf(buf, "lx=%s cx=%s rx=%s\nty=%s my=%s by=%s\nw=%s h=%s",
1268             lx_buf, cx_buf, rx_buf, ty_buf, my_buf, by_buf, w_buf, h_buf);
1269    }
1270    SetMeasureTooltip(buf);
1271 }
1272 
StartMeasureTooltip(msg)1273 void StartMeasureTooltip(msg)
1274    char *msg;
1275 {
1276    SetMeasureTooltip(msg);
1277 }
1278 
SetMeasureTooltip(msg)1279 void SetMeasureTooltip(msg)
1280    char *msg;
1281 {
1282    int x=0, y=0;
1283    char *psz=NULL, *line=NULL;
1284 
1285    if (!showMeasurementInTooltip) return;
1286 
1287    if (!gmti.mapped) {
1288       XMapWindow(mainDisplay, gmti.win);
1289       gmti.mapped = TRUE;
1290    }
1291    XRaiseWindow(mainDisplay, gmti.win);
1292 
1293    SetMeasureTooltipBBox(msg);
1294    XMoveResizeWindow(mainDisplay, gmti.win, gmti.ltx, gmti.lty, gmti.w, gmti.h);
1295 
1296    XClearWindow(mainDisplay, gmti.win);
1297 
1298    x = gmti.x_padding;
1299    y = gmti.y_padding + ((msgFontSet==NULL && msgFontPtr==NULL) ?
1300          defaultFontAsc : msgFontAsc);;
1301    line = msg;
1302    psz = strchr(line, '\n');
1303    while (line != NULL) {
1304       char saved_ch='\0';
1305 
1306       if (psz != NULL) {
1307          saved_ch = (*psz);
1308          *psz = '\0';
1309       }
1310       DrawMsgString(mainDisplay, gmti.win, gmti.gc, x, y, line, strlen(line));
1311       if (psz == NULL) {
1312          break;
1313       }
1314       *psz = saved_ch;
1315       line = &psz[1];
1316       psz = strchr(line, '\n');
1317 
1318       y += ((msgFontSet==NULL && msgFontPtr==NULL) ? defaultFontHeight :
1319             msgFontHeight);
1320    }
1321 }
1322 
EndMeasureTooltip(force)1323 void EndMeasureTooltip(force)
1324    int force;
1325 {
1326    if (force || (showMeasurement && showMeasurementInTooltip)) {
1327       if (gmti.mapped) {
1328          XUnmapWindow(mainDisplay, gmti.win);
1329          gmti.mapped = FALSE;
1330       }
1331    }
1332 }
1333 
CleanUpMeasureTooltip()1334 void CleanUpMeasureTooltip()
1335 {
1336    if (tooltipBgPixmap != None) {
1337       XFreePixmap(mainDisplay, tooltipBgPixmap);
1338       tooltipBgPixmap = None;
1339    }
1340    if (gmti.gc != NULL) {
1341       XFreeGC(mainDisplay, gmti.gc);
1342    }
1343    if (gmti.win != None) {
1344       XDestroyWindow(mainDisplay, gmti.win);
1345    }
1346    memset(&gmti, 0, sizeof(MeasureTooltipInfo));
1347    showMeasurementInTooltip = FALSE;
1348    gmti.win = None;
1349 }
1350 
1351 static
CreateMeasureTooltipWindow()1352 int CreateMeasureTooltipWindow()
1353 {
1354    int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel);
1355    XGCValues values;
1356    XWMHints wmhints;
1357    XSizeHints sizehints;
1358    XSetWindowAttributes win_attrs;
1359 
1360    if ((gmti.win=XCreateSimpleWindow(mainDisplay, rootWindow, 0, 0, 10, 10,
1361          1, myBorderPixel, bg_pixel)) == 0) {
1362       return FailToCreateWindowMessage("CreateMeasureTooltipWindow()", NULL,
1363             FALSE);
1364    }
1365    win_attrs.save_under = True;
1366    win_attrs.override_redirect = True;
1367    win_attrs.colormap = mainColormap;
1368    XChangeWindowAttributes(mainDisplay, gmti.win,
1369          CWSaveUnder | CWOverrideRedirect | CWColormap, &win_attrs);
1370 
1371    wmhints.flags = InputHint | StateHint;
1372    wmhints.input = True;
1373    wmhints.initial_state = NormalState;
1374    XSetWMHints(mainDisplay, gmti.win, &wmhints);
1375 
1376    sizehints.flags = PPosition | PSize | USPosition | PMinSize | PMaxSize;
1377    sizehints.x = 0;
1378    sizehints.y = 0;
1379    sizehints.width = sizehints.min_width = sizehints.max_width = 10;
1380    sizehints.height = sizehints.min_height = sizehints.max_height = 10;
1381 #ifdef NOTR4MODE
1382    XSetNormalHints(mainDisplay, gmti.win, &sizehints);
1383 #else
1384    XSetWMNormalHints(mainDisplay, gmti.win, &sizehints);
1385 #endif
1386 
1387    XSetTransientForHint(mainDisplay, gmti.win, mainWindow);
1388 
1389    values.foreground = myFgPixel;
1390    values.background = bg_pixel;
1391    values.font = (msgFontPtr==NULL ? defaultFontPtr->fid : msgFontPtr->fid);
1392    gmti.gc = XCreateGC(mainDisplay, gmti.win,
1393          GCForeground | GCBackground | GCFont, &values);
1394    return TRUE;
1395 }
1396 
1397 static
CreateLightYellowBackgroundPixmapForTooltip()1398 void CreateLightYellowBackgroundPixmapForTooltip()
1399 {
1400    XImage *image=NULL;
1401 
1402    tooltipBgPixmap = XCreatePixmap(mainDisplay, mainWindow, 2, 2, mainDepth);
1403    XSetForeground(mainDisplay, xpmGC, myYellowPixel);
1404    XFillRectangle(mainDisplay, tooltipBgPixmap, xpmGC, 0, 0, 2, 2);
1405    XSetForeground(mainDisplay, xpmGC, myFgPixel);
1406    image = XGetImage(mainDisplay, tooltipBgPixmap, 0, 0, 2, 2, AllPlanes,
1407          ZPixmap);
1408    XPutPixel(image, 0, 0, myWhitePixel);
1409    XPutPixel(image, 1, 1, myWhitePixel);
1410    XPutImage(mainDisplay, tooltipBgPixmap, xpmGC, image, 0, 0, 0, 0, 2, 2);
1411    XDestroyImage(image);
1412 
1413    XSetWindowBackgroundPixmap(mainDisplay, gmti.win, tooltipBgPixmap);
1414 }
1415 
InitMeasureTooltip()1416 int InitMeasureTooltip()
1417 {
1418    char *c_ptr=NULL;
1419 
1420    tooltipBgPixmap = None;
1421 
1422    memset(&gmti, 0, sizeof(MeasureTooltipInfo));
1423    gmti.win = None;
1424    gmti.mapped = FALSE;
1425    gmti.x_padding = 4;
1426    gmti.y_padding = 2;
1427 
1428    showMeasurementInTooltip = FALSE;
1429    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1430          "UseMeasureTooltip")) != NULL &&
1431          UtilStrICmp(c_ptr, "true") == 0) {
1432       showMeasurementInTooltip = TRUE;
1433    }
1434    measureTooltipVerbose = FALSE;
1435    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1436          "MeasureTooltipVerbose")) != NULL &&
1437          UtilStrICmp(c_ptr, "true") == 0) {
1438       measureTooltipVerbose = TRUE;
1439    }
1440    gmti.x_follow_mouse = gmti.y_follow_mouse = FALSE;
1441    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1442          "MeasureTooltipXFollowMouse")) != NULL &&
1443          UtilStrICmp(c_ptr, "true") == 0) {
1444       gmti.x_follow_mouse = TRUE;
1445    }
1446    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1447          "MeasureTooltipYFollowMouse")) != NULL &&
1448          UtilStrICmp(c_ptr, "true") == 0) {
1449       gmti.y_follow_mouse = TRUE;
1450    }
1451    gmti.position_y = ALIGN_T;
1452    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1453          "MeasureTooltipVerticalPosition")) != NULL) {
1454       if (UtilStrICmp(c_ptr, "top") == 0) {
1455          gmti.position_y = ALIGN_T;
1456       } else if (UtilStrICmp(c_ptr, "middle") == 0) {
1457          gmti.position_y = ALIGN_M;
1458       } else if (UtilStrICmp(c_ptr, "bottom") == 0) {
1459          gmti.position_y = ALIGN_B;
1460       } else {
1461          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
1462                TOOL_NAME, "MeasureTooltipVerticalPosition", c_ptr, "Top");
1463          fprintf(stderr, "\n");
1464       }
1465    }
1466    gmti.position_x = ALIGN_L;
1467    if ((c_ptr=XGetDefault(mainDisplay, TOOL_NAME,
1468          "MeasureTooltipHorizontalPosition")) != NULL) {
1469       if (UtilStrICmp(c_ptr, "left") == 0) {
1470          gmti.position_x = ALIGN_L;
1471       } else if (UtilStrICmp(c_ptr, "center") == 0) {
1472          gmti.position_x = ALIGN_C;
1473       } else if (UtilStrICmp(c_ptr, "right") == 0) {
1474          gmti.position_x = ALIGN_R;
1475       } else {
1476          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
1477                TOOL_NAME, "MeasureTooltipHorizontalPosition", c_ptr, "Left");
1478          fprintf(stderr, "\n");
1479       }
1480    }
1481    if (!CreateMeasureTooltipWindow()) {
1482       gmti.win = None;
1483    } else if (threeDLook) {
1484       CreateLightYellowBackgroundPixmapForTooltip();
1485    }
1486    return TRUE;
1487 }
1488