1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <fontforge-config.h>
29
30 #include "cvruler.h"
31 #include "fontforgeui.h"
32 #include "splineutil.h"
33 #include "splineutil2.h"
34 #include "ustring.h"
35
36 #include <math.h>
37
38 int measuretoolshowhorizontolvertical = true;
39 Color measuretoollinecol = 0x000000;
40 Color measuretoolpointcol = 0xFF0000;
41 Color measuretoolpointsnappedcol = 0x00FF00;
42 Color measuretoolcanvasnumberscol = 0xFF0000;
43 Color measuretoolcanvasnumberssnappedcol = 0x00FF00;
44 Color measuretoolwindowforegroundcol = 0x000000;
45 Color measuretoolwindowbackgroundcol = 0xe0e0c0;
46
47 BasePoint last_ruler_offset[2] = { {0,0}, {0,0} };
48 int infowindowdistance = 30;
49
SlopeToBuf(char * buf,char * label,double dx,double dy)50 static void SlopeToBuf(char *buf,char *label,double dx, double dy) {
51 if ( dx==0 && dy==0 )
52 sprintf( buf, _("%s No Slope"), label );
53 else if ( dx==0 )
54 sprintf( buf, "%s dy/dx= ∞, %4g°", label, atan2(dy,dx)*180/FF_PI);
55 else
56 sprintf( buf, "%s dy/dx= %4g, %4g°", label, dy/dx, atan2(dy,dx)*180/FF_PI);
57 }
58
CurveToBuf(char * buf,CharView * cv,Spline * s,double t)59 static void CurveToBuf(char *buf,CharView *cv,Spline *s, double t) {
60 double kappa, emsize;
61
62 kappa = SplineCurvature(s,t);
63 if ( kappa==CURVATURE_ERROR )
64 strcpy(buf,_("No Curvature"));
65 else {
66 emsize = cv->b.sc->parent->ascent + cv->b.sc->parent->descent;
67 if ( kappa==0 )
68 sprintf(buf,_(" Curvature: %g"), kappa*emsize);
69 else
70 sprintf(buf,_(" Curvature: %g Radius: %g"), kappa*emsize, 1.0/kappa );
71 }
72 }
73
RulerText(CharView * cv,unichar_t * ubuf,int line)74 static int RulerText(CharView *cv, unichar_t *ubuf, int line) {
75 char buf[80];
76 double len;
77 double dx, dy;
78 Spline *s;
79 double t;
80 BasePoint slope;
81 real xoff = cv->info.x-cv->p.cx, yoff = cv->info.y-cv->p.cy;
82
83 buf[0] = '\0';
84 switch ( line ) {
85 case 0: {
86 real len = sqrt(xoff*xoff+yoff*yoff);
87
88 if ( cv->autonomous_ruler_w ) {
89 xoff = last_ruler_offset[0].x;
90 yoff = last_ruler_offset[0].y;
91 }
92
93 if ( !cv->autonomous_ruler_w && !cv->p.pressed )
94 /* Give current location accurately */
95 sprintf( buf, "%f,%f", (double) cv->info.x, (double) cv->info.y);
96 else
97 sprintf( buf, "%f %.0f° (%f,%f)", (double) len,
98 atan2(yoff,xoff)*180/FF_PI,
99 (double) xoff,(double) yoff);
100 break; }
101 case 1:
102 if ( cv->p.pressed ) {
103 if ( cv->p.spline!=NULL ||
104 (cv->p.sp!=NULL &&
105 ((cv->p.sp->next==NULL && cv->p.sp->prev!=NULL) ||
106 (cv->p.sp->prev==NULL && cv->p.sp->next!=NULL) ||
107 (cv->p.sp->next!=NULL && cv->p.sp->prev!=NULL &&
108 BpColinear(!cv->p.sp->noprevcp ? &cv->p.sp->prevcp : &cv->p.sp->prev->from->me,
109 &cv->p.sp->me,
110 !cv->p.sp->nonextcp ? &cv->p.sp->nextcp : &cv->p.sp->next->to->me))) ) ) {
111 Spline *spline;
112 double t;
113
114 if ( cv->p.spline!=NULL ) {
115 spline = cv->p.spline;
116 t = cv->p.t;
117 } else if ( cv->p.sp->next == NULL ) {
118 spline = cv->p.sp->prev;
119 t = 1;
120 } else {
121 spline = cv->p.sp->next;
122 t = 0;
123 }
124 slope.x = (3*spline->splines[0].a*t + 2*spline->splines[0].b)*t + spline->splines[0].c;
125 slope.y = (3*spline->splines[1].a*t + 2*spline->splines[1].b)*t + spline->splines[1].c;
126 len = sqrt(slope.x*slope.x + slope.y*slope.y);
127 if ( len!=0 ) {
128 slope.x /= len; slope.y /= len;
129 sprintf( buf, _("Normal Distance: %.2f Along Spline: %.2f"),
130 fabs(slope.y*xoff - slope.x*yoff),
131 slope.x*xoff + slope.y*yoff );
132 }
133 }
134 } else if ( cv->dv!=NULL || cv->b.gridfit!=NULL ) {
135 double scalex = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent)/(rint(cv->ft_pointsizex*cv->ft_dpi/72.0));
136 double scaley = (cv->b.sc->parent->ascent+cv->b.sc->parent->descent)/(rint(cv->ft_pointsizey*cv->ft_dpi/72.0));
137 sprintf( buf, "%.2f,%.2f", (double) (cv->info.x/scalex), (double) (cv->info.y/scaley));
138 } else if ( cv->p.spline!=NULL ) {
139 s = cv->p.spline;
140 t = cv->p.t;
141 sprintf( buf, _("Near (%f,%f)"),
142 (double) (((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d),
143 (double) (((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d) );
144 } else if ( cv->p.sp!=NULL ) {
145 sprintf( buf, _("Near (%f,%f)"),(double) cv->p.sp->me.x,(double) cv->p.sp->me.y );
146 } else
147 return( false );
148 break;
149 case 2:
150 if ( cv->p.pressed ) {
151 if ( cv->p.sp!=NULL && cv->info_sp!=NULL &&
152 ((cv->p.sp->next!=NULL && cv->p.sp->next->to==cv->info_sp) ||
153 (cv->p.sp->prev!=NULL && cv->p.sp->prev->from==cv->info_sp)) ) {
154 if ( cv->p.sp->next!=NULL && cv->p.sp->next->to==cv->info_sp )
155 len = SplineLength(cv->p.sp->next);
156 else
157 len = SplineLength(cv->p.sp->prev);
158 } else if ( cv->p.spline == cv->info_spline && cv->info_spline!=NULL )
159 len = SplineLengthRange(cv->info_spline,cv->p.t,cv->info_t);
160 else if ( cv->p.sp!=NULL && cv->info_spline!=NULL &&
161 cv->p.sp->next == cv->info_spline )
162 len = SplineLengthRange(cv->info_spline,0,cv->info_t);
163 else if ( cv->p.sp!=NULL && cv->info_spline!=NULL &&
164 cv->p.sp->prev == cv->info_spline )
165 len = SplineLengthRange(cv->info_spline,cv->info_t,1);
166 else if ( cv->info_sp!=NULL && cv->p.spline!=NULL &&
167 cv->info_sp->next == cv->p.spline )
168 len = SplineLengthRange(cv->p.spline,0,cv->p.t);
169 else if ( cv->info_sp!=NULL && cv->p.spline!=NULL &&
170 cv->info_sp->prev == cv->p.spline )
171 len = SplineLengthRange(cv->p.spline,cv->p.t,1);
172 else
173 return( false );
174 if ( len>1 )
175 sprintf( buf, _("Spline Length=%.1f"), len);
176 else
177 sprintf( buf, _("Spline Length=%g"), len);
178 } else if ( cv->p.spline!=NULL ) {
179 s = cv->p.spline;
180 t = cv->p.t;
181 dx = (3*s->splines[0].a*t+2*s->splines[0].b)*t+s->splines[0].c;
182 dy = (3*s->splines[1].a*t+2*s->splines[1].b)*t+s->splines[1].c;
183 SlopeToBuf(buf,"",dx,dy);
184 } else if ( cv->p.sp!=NULL ) {
185 if ( cv->p.sp->nonextcp )
186 strcpy(buf,_("No Next Control Point"));
187 else
188 sprintf(buf,_("Next CP: (%f,%f)"), (double) cv->p.sp->nextcp.x, (double) cv->p.sp->nextcp.y);
189 } else
190 return( false );
191 break;
192 case 3:
193 if ( cv->p.pressed )
194 return( false );
195 else if ( cv->p.spline!=NULL ) {
196 CurveToBuf(buf,cv,cv->p.spline,cv->p.t);
197 } else if ( cv->p.sp!=NULL && cv->p.sp->next!=NULL ) {
198 s = cv->p.sp->next;
199 dx = s->splines[0].c;
200 dy = s->splines[1].c;
201 SlopeToBuf(buf,_(" Next"),dx,dy);
202 } else if ( cv->p.sp!=NULL ) {
203 if ( cv->p.sp->noprevcp )
204 strcpy(buf,_("No Previous Control Point"));
205 else
206 sprintf(buf,_("Prev CP: (%f,%f)"), (double) cv->p.sp->prevcp.x, (double) cv->p.sp->prevcp.y);
207 } else
208 return( false );
209 break;
210 case 4:
211 if ( cv->p.spline!=NULL )
212 return( false );
213 else if ( cv->p.sp->next!=NULL ) {
214 CurveToBuf(buf,cv,cv->p.sp->next,0);
215 } else if ( cv->p.sp->prev!=NULL ) {
216 s = cv->p.sp->prev;
217 dx = (3*s->splines[0].a*1+2*s->splines[0].b)*1+s->splines[0].c;
218 dy = (3*s->splines[1].a*1+2*s->splines[1].b)*1+s->splines[1].c;
219 SlopeToBuf(buf,_(" Prev"),dx,dy);
220 } else
221 return( false );
222 break;
223 case 5:
224 if ( cv->p.sp->next!=NULL ) {
225 if ( cv->p.sp->noprevcp )
226 strcpy(buf,_("No Previous Control Point"));
227 else
228 sprintf(buf,_("Prev CP: (%f,%f)"), (double) cv->p.sp->prevcp.x, (double) cv->p.sp->prevcp.y);
229 } else {
230 CurveToBuf(buf,cv,cv->p.sp->prev,1);
231 }
232 break;
233 case 6:
234 if ( cv->p.sp->next!=NULL && cv->p.sp->prev!=NULL ) {
235 s = cv->p.sp->prev;
236 dx = (3*s->splines[0].a*1+2*s->splines[0].b)*1+s->splines[0].c;
237 dy = (3*s->splines[1].a*1+2*s->splines[1].b)*1+s->splines[1].c;
238 SlopeToBuf(buf,_(" Prev"),dx,dy);
239 } else
240 return( false );
241 break;
242 case 7:
243 if ( cv->p.sp->next!=NULL && cv->p.sp->prev!=NULL ) {
244 CurveToBuf(buf,cv,cv->p.sp->prev,1);
245 } else
246 return( false );
247 break;
248 default:
249 return( false );
250 }
251 utf82u_strcpy(ubuf,buf);
252 return( true );
253 }
254
RulerTextIntersection(CharView * cv,unichar_t * ubuf,int i)255 static int RulerTextIntersection(CharView *cv, unichar_t *ubuf, int i) {
256 char buf[80];
257
258 if ( i==0 && cv->num_ruler_intersections>4 ) {
259 real xoff = cv->ruler_intersections[cv->num_ruler_intersections-2].x - cv->ruler_intersections[1].x;
260 real yoff = cv->ruler_intersections[cv->num_ruler_intersections-2].y - cv->ruler_intersections[1].y;
261 real len = sqrt(xoff*xoff+yoff*yoff);
262 snprintf(buf,sizeof buf, _("First Edge to Last Edge: %g x %g length %f"),fabs(xoff),fabs(yoff),len);
263 utf82u_strcpy(ubuf,buf);
264 return( 1 );
265 } else if ( cv->num_ruler_intersections>4 )
266 i -= 1;
267
268 if ( i>=cv->num_ruler_intersections )
269 return( 0 );
270
271 if ( i==0 ) {
272 snprintf(buf,sizeof buf,"[%d] (%g,%g)",i,cv->ruler_intersections[i].x,cv->ruler_intersections[i].y);
273 if ( cv->p.sp ) {
274 strcat(buf, _(" snapped"));
275 cv->start_intersection_snapped = 1;
276 } else {
277 cv->start_intersection_snapped = 0;
278 }
279 } else {
280 real xoff = cv->ruler_intersections[i].x - cv->ruler_intersections[i-1].x;
281 real yoff = cv->ruler_intersections[i].y - cv->ruler_intersections[i-1].y;
282 real len = sqrt(xoff*xoff+yoff*yoff);
283 snprintf(buf,sizeof buf, _("[%d] (%g,%g) %g x %g length %g"),i,cv->ruler_intersections[i].x,cv->ruler_intersections[i].y,fabs(xoff),fabs(yoff),len);
284 if ( i==(cv->num_ruler_intersections-1) ) {
285 if ( cv->info_sp ) {
286 strcat(buf, _(" snapped"));
287 cv->end_intersection_snapped = 1;
288 } else {
289 cv->end_intersection_snapped = 0;
290 }
291 }
292 }
293
294 utf82u_strcpy(ubuf,buf);
295 return( 1 );
296 }
297
ruler_e_h(GWindow gw,GEvent * event)298 static int ruler_e_h(GWindow gw, GEvent *event) {
299 CharView *cv = (CharView *) GDrawGetUserData(gw);
300 unichar_t ubuf[80];
301 int line;
302 int i;
303
304 switch ( event->type ) {
305 case et_expose:
306 GDrawSetFont(gw,cv->rfont);
307 for ( line=0; RulerText(cv,ubuf,line); ++line )
308 GDrawDrawText(gw,2,line*cv->rfh+cv->ras+1,ubuf,-1,measuretoolwindowforegroundcol);
309 if ( cv->p.pressed ) for ( i=0; RulerTextIntersection(cv,ubuf,i); ++i )
310 GDrawDrawText(gw,2,(line+i)*cv->rfh+cv->ras+1,ubuf,-1,measuretoolwindowforegroundcol);
311 break;
312 case et_mousedown:
313 cv->autonomous_ruler_w = false;
314 GDrawDestroyWindow(gw);
315 cv->ruler_w = NULL;
316 break;
317 }
318 return( true );
319 }
320
ruler_linger_e_h(GWindow gw,GEvent * event)321 static int ruler_linger_e_h(GWindow gw, GEvent *event) {
322 CharView *cv = (CharView *) GDrawGetUserData(gw);
323 int i;
324
325 switch ( event->type ) {
326 case et_expose:
327 GDrawSetFont(gw,cv->rfont);
328 for ( i=0; i < cv->ruler_linger_num_lines ; ++i )
329 GDrawDrawText(gw,2,i*cv->rfh+cv->ras+1,cv->ruler_linger_lines[i],-1,measuretoolwindowforegroundcol);
330 break;
331 case et_mousedown:
332 // TBD
333 // cv->autonomous_ruler_w = false;
334 // GDrawDestroyWindow(gw);
335 // cv->ruler_linger_w = NULL;
336 break;
337 }
338 return( true );
339 }
340
341 static GFont *rvfont=NULL;
342
343 /*
344 * Comparison function for use with qsort.
345 */
BasePointCompare(const void * _l,const void * _r)346 static int BasePointCompare(const void *_l, const void *_r) {
347 const BasePoint *l = _l, *r = _r;
348 if ( l->x>r->x)
349 return( 1 );
350 if ( l->x<r->x)
351 return( -1 );
352 if ( l->y>r->y)
353 return( 1 );
354 if ( l->y<r->y)
355 return( -1 );
356 return( 0 );
357 }
358
ReverseBasePointCompare(const void * l,const void * r)359 static int ReverseBasePointCompare(const void *l, const void *r) {
360 return( -BasePointCompare(l,r) );
361 }
362
363 /*
364 * Fill buffer with intersects on a line (from,to).
365 * return number found, buf fill the buffer only up to a max_intersections.
366 *
367 * The points from and to are also put in the buffer.
368 *
369 * Copied somewhat from CVMouseUpKnife(), perhaps they should be consolidated
370 */
GetIntersections(CharView * cv,BasePoint from,BasePoint to,BasePoint * all_intersections,int max_intersections)371 static int GetIntersections(CharView *cv,BasePoint from,BasePoint to,BasePoint *all_intersections,int max_intersections) {
372 SplineSet *spl;
373 Spline *s, *nexts;
374 Spline dummy;
375 SplinePoint dummyfrom, dummyto;
376 BasePoint inters[9]; /* These bounds are hard coded in the SplinesIntersect function */
377 extended t1s[10], t2s[10];
378 int i;
379 int total_intersections = 0;
380
381 memset(&dummy,0,sizeof(dummy));
382 memset(&dummyfrom,0,sizeof(dummyfrom));
383 memset(&dummyto,0,sizeof(dummyto));
384 dummyfrom.me.x = from.x; dummyfrom.me.y = from.y;
385 dummyto.me.x = to.x; dummyto.me.y = to.y;
386 dummyfrom.nextcp = dummyfrom.prevcp = dummyfrom.me;
387 dummyto.nextcp = dummyto.prevcp = dummyto.me;
388 dummyfrom.nonextcp = dummyfrom.noprevcp = dummyto.nonextcp = dummyto.noprevcp = true;
389 dummy.splines[0].d = from.x; dummy.splines[0].c = to.x-from.x;
390 dummy.splines[1].d = from.y; dummy.splines[1].c = to.y-from.y;
391 dummy.from = &dummyfrom; dummy.to = &dummyto;
392 dummy.islinear = dummy.knownlinear = true;
393 dummyfrom.next = dummyto.prev = &dummy;
394
395 all_intersections[total_intersections++] = from;
396
397 for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
398 for ( s = spl->first->next; s!=NULL ; ) {
399 nexts = NULL;
400 if ( s->to!=spl->first )
401 nexts = s->to->next;
402
403 if ( SplinesIntersect(s,&dummy,inters,t1s,t2s)>0 ) {
404 /* TBD, why is 4 used here (copied from CVMouseUpKnife()) */
405 for ( i=0; i<4 && t1s[i]!=-1; ++i ) {
406 if ( (total_intersections+1)<max_intersections )
407 all_intersections[total_intersections] = inters[i];
408 total_intersections++;
409 }
410 }
411 s = nexts;
412 }
413 }
414
415 if ( total_intersections<max_intersections )
416 all_intersections[total_intersections++] = to;
417
418 qsort(all_intersections,
419 total_intersections > max_intersections ? max_intersections : total_intersections,
420 sizeof(all_intersections[0]),
421 BasePointCompare(&from,&to)<=0 ? BasePointCompare : ReverseBasePointCompare );
422
423 /*
424 * Filter out intersectsions that are too close.
425 * This is for snapped points, but we get more than one extra per snap,
426 * so do them all for now.
427 */
428 for ( i = 1 ; i<total_intersections && i<max_intersections ; ) {
429 if ( (0.00001 > fabs(all_intersections[i].x-all_intersections[i-1].x)) &&
430 (0.00001 > fabs(all_intersections[i].y-all_intersections[i-1].y)) ) {
431 int j;
432
433 for( j = i+1 ; j<total_intersections && j<max_intersections ; j++ )
434 all_intersections[j-1] = all_intersections[j];
435 if ( total_intersections < max_intersections )
436 total_intersections--;
437 } else {
438 i++;
439 }
440 }
441
442 return( total_intersections ); /* note that it could be greater than max */
443 }
444
RulerPlace(CharView * cv,GEvent * event)445 static void RulerPlace(CharView *cv, GEvent *event) {
446 unichar_t ubuf[80];
447 int width, x, y;
448 GRect size;
449 GPoint pt;
450 int i,h,w;
451 GWindowAttrs wattrs;
452 GRect pos;
453 FontRequest rq;
454 int as, ds, ld;
455
456 if ( cv->ruler_w==NULL ) {
457 memset(&wattrs,0,sizeof(wattrs));
458 wattrs.mask = wam_events|wam_cursor|wam_positioned|wam_nodecor|wam_backcol|wam_bordwidth;
459 wattrs.event_masks = (1<<et_expose)|(1<<et_resize)|(1<<et_mousedown);
460 wattrs.cursor = ct_mypointer;
461 wattrs.background_color = measuretoolwindowbackgroundcol;
462 wattrs.nodecoration = 1;
463 wattrs.border_width = 1;
464 pos.x = pos.y = 0; pos.width=pos.height = 20;
465 cv->ruler_w = GWidgetCreateTopWindow(NULL,&pos,ruler_e_h,cv,&wattrs);
466
467 if ( rvfont==NULL ) {
468 memset(&rq,0,sizeof(rq));
469 rq.utf8_family_name = FIXED_UI_FAMILIES;
470 rq.point_size = -12;
471 rq.weight = 400;
472 rvfont = GDrawInstanciateFont(cv->ruler_w,&rq);
473 rvfont = GResourceFindFont("CharView.Measure.Font",rvfont);
474 }
475 cv->rfont = rvfont;
476 GDrawWindowFontMetrics(cv->ruler_w,cv->rfont,&as,&ds,&ld);
477 cv->rfh = as+ds; cv->ras = as;
478 } else
479 GDrawRaise(cv->ruler_w);
480
481 if ( cv->p.pressed ) {
482 BasePoint from;
483
484 from.x = cv->p.cx;
485 from.y = cv->p.cy;
486
487 if ( !cv->ruler_intersections ) {
488 cv->allocated_ruler_intersections = 32;
489 cv->ruler_intersections = malloc(cv->allocated_ruler_intersections * sizeof(cv->ruler_intersections[0]));
490 }
491 for(;;) {
492 cv->num_ruler_intersections = GetIntersections(cv,from,cv->info,cv->ruler_intersections,cv->allocated_ruler_intersections);
493 if ( cv->num_ruler_intersections>cv->allocated_ruler_intersections ) {
494 cv->allocated_ruler_intersections = cv->num_ruler_intersections * 2;
495 cv->ruler_intersections = realloc(cv->ruler_intersections,cv->allocated_ruler_intersections * sizeof(cv->ruler_intersections[0]));
496 } else
497 break;
498 }
499 }
500
501 GDrawSetFont(cv->ruler_w,cv->rfont);
502 width = h = 0;
503 for ( i=0; RulerText(cv,ubuf,i); ++i ) {
504 w = GDrawGetTextWidth(cv->ruler_w,ubuf,-1);
505 if ( w>width ) width = w;
506 h += cv->rfh;
507 }
508 if ( cv->p.pressed ) for ( i=0; RulerTextIntersection(cv,ubuf,i); ++i ) {
509 w = GDrawGetTextWidth(cv->ruler_w,ubuf,-1);
510 if ( w>width ) width = w;
511 h += cv->rfh;
512 }
513
514 GDrawGetSize(GDrawGetRoot(NULL),&size);
515 pt.x = event->u.mouse.x; pt.y = event->u.mouse.y;
516 GDrawTranslateCoordinates(cv->v,GDrawGetRoot(NULL),&pt);
517 x = pt.x + infowindowdistance;
518 if ( x+width > size.width )
519 x = pt.x - width-infowindowdistance;
520 y = pt.y -cv->ras-2;
521 if ( y+h > size.height )
522 y = pt.y - h - cv->ras -10;
523 GDrawMoveResize(cv->ruler_w,x,y,width+4,h+4);
524 }
525
RulerLingerPlace(CharView * cv,GEvent * event)526 static void RulerLingerPlace(CharView *cv, GEvent *event) {
527 int width, x, y;
528 GRect size;
529 GPoint pt;
530 int i,h,w;
531 GWindowAttrs wattrs;
532 GRect pos;
533 FontRequest rq;
534 int as, ds, ld;
535 int line;
536 int old_pressed;
537
538 if ( cv->ruler_linger_w==NULL ) {
539 memset(&wattrs,0,sizeof(wattrs));
540 wattrs.mask = wam_events|wam_cursor|wam_positioned|wam_nodecor|wam_backcol|wam_bordwidth;
541 wattrs.event_masks = (1<<et_expose)|(1<<et_resize)|(1<<et_mousedown);
542 wattrs.cursor = ct_mypointer;
543 wattrs.background_color = measuretoolwindowbackgroundcol;
544 wattrs.nodecoration = 1;
545 wattrs.border_width = 1;
546 pos.x = pos.y = 0; pos.width=pos.height = 20;
547 cv->ruler_linger_w = GWidgetCreateTopWindow(NULL,&pos,ruler_linger_e_h,cv,&wattrs);
548
549 if ( rvfont==NULL ) {
550 memset(&rq,0,sizeof(rq));
551 rq.utf8_family_name = FIXED_UI_FAMILIES;
552 rq.point_size = -12;
553 rq.weight = 400;
554 rvfont = GDrawInstanciateFont(cv->ruler_w,&rq);
555 rvfont = GResourceFindFont("CharView.Measure.Font",rvfont);
556 }
557 cv->rfont = rvfont;
558 GDrawWindowFontMetrics(cv->ruler_linger_w,cv->rfont,&as,&ds,&ld);
559 cv->rfh = as+ds; cv->ras = as;
560 } else
561 GDrawRaise(cv->ruler_linger_w);
562
563 GDrawSetFont(cv->ruler_linger_w,cv->rfont);
564 width = h = 0;
565 line = 0;
566 old_pressed = cv->p.pressed;
567 cv->p.pressed = true;
568
569 for ( i=0; line<sizeof(cv->ruler_linger_lines)/sizeof(cv->ruler_linger_lines[0]) && RulerText(cv,cv->ruler_linger_lines[line],i) ; ++i,++line ) {
570 w = GDrawGetTextWidth(cv->ruler_linger_w,cv->ruler_linger_lines[line],-1);
571 if ( w>width ) width = w;
572 h += cv->rfh;
573 }
574 cv->p.pressed = old_pressed;
575 for ( i=0; line<sizeof(cv->ruler_linger_lines)/sizeof(cv->ruler_linger_lines[0]) && RulerTextIntersection(cv,cv->ruler_linger_lines[line],i); ++i,++line ) {
576 w = GDrawGetTextWidth(cv->ruler_linger_w,cv->ruler_linger_lines[line],-1);
577 if ( w>width ) width = w;
578 h += cv->rfh;
579 }
580 cv->ruler_linger_num_lines = line;
581
582 GDrawGetSize(GDrawGetRoot(NULL),&size);
583 pt.x = event->u.mouse.x; pt.y = event->u.mouse.y;
584 GDrawTranslateCoordinates(cv->v,GDrawGetRoot(NULL),&pt);
585 x = pt.x + infowindowdistance;
586 if ( x+width > size.width )
587 x = pt.x - width-infowindowdistance;
588 y = pt.y -cv->ras-2;
589 if ( y+h > size.height )
590 y = pt.y - h - cv->ras -10;
591 GDrawMoveResize(cv->ruler_linger_w,x,y,width+4,h+4);
592 GDrawSetVisible(cv->ruler_linger_w,true);
593 }
594
RulerLingerMove(CharView * cv)595 static void RulerLingerMove(CharView *cv) {
596 CharViewTab* tab = CVGetActiveTab(cv);
597 if ( cv->ruler_linger_w ) {
598 int x, y;
599 GRect size;
600 GRect rsize;
601 GRect csize;
602 GPoint pt;
603
604 GDrawGetSize(GDrawGetRoot(NULL),&size);
605 GDrawGetSize(cv->ruler_linger_w,&rsize);
606 GDrawGetSize(cv->gw,&csize);
607
608 pt.x = tab->xoff + rint(cv->ruler_intersections[cv->num_ruler_intersections-1].x*tab->scale);
609 pt.y = -tab->yoff + cv->height - rint(cv->ruler_intersections[cv->num_ruler_intersections-1].y*tab->scale);
610 GDrawTranslateCoordinates(cv->v,GDrawGetRoot(NULL),&pt);
611 x = pt.x + infowindowdistance;
612 if ( x+rsize.width>size.width )
613 x = pt.x - rsize.width-infowindowdistance;
614 y = pt.y -cv->ras-2;
615 if ( y+rsize.height>size.height )
616 y = pt.y - rsize.height - cv->ras -10;
617
618 if ( x>=csize.x && x<=(csize.x+csize.width) && y>=csize.y && y<=(csize.y+csize.height) ) {
619 GDrawMove(cv->ruler_linger_w,x,y);
620 GDrawSetVisible(cv->ruler_linger_w,true);
621 } else {
622 GDrawSetVisible(cv->ruler_linger_w,false);
623 }
624 }
625 }
626
CVMouseDownRuler(CharView * cv,GEvent * event)627 void CVMouseDownRuler(CharView *cv, GEvent *event) {
628
629 cv->autonomous_ruler_w = false;
630
631 RulerPlace(cv,event);
632 cv->p.rubberlining = true;
633 GDrawSetVisible(cv->ruler_w,true);
634 if ( cv->ruler_linger_w ) {
635 GDrawDestroyWindow(cv->ruler_linger_w);
636 cv->ruler_linger_w = NULL;
637 }
638 }
639
CVMouseMoveRuler(CharView * cv,GEvent * event)640 void CVMouseMoveRuler(CharView *cv, GEvent *event) {
641 if ( cv->autonomous_ruler_w )
642 return;
643
644 if ( !cv->p.pressed && (event->u.mouse.state&ksm_meta) ) {
645 if ( cv->ruler_w!=NULL && GDrawIsVisible(cv->ruler_w)) {
646 GDrawDestroyWindow(cv->ruler_w);
647 cv->ruler_w = NULL;
648 }
649 return;
650 }
651 if ( !cv->p.pressed )
652 CVMouseAtSpline(cv,event);
653 RulerPlace(cv,event);
654 if ( !cv->p.pressed )
655 GDrawSetVisible(cv->ruler_w,true);
656 GDrawSync(NULL);
657 // The following code may be unnecessary, and it can cause an infinite stack loop.
658 // One would hope that the event queue can take care of these things when we return to it.
659 // We'll find out.
660 //#if 0
661 // GDrawProcessPendingEvents(NULL); /* The resize needs to happen before the expose */
662 // if ( !cv->p.pressed && (event->u.mouse.state&ksm_meta) ) /* but a mouse up might sneak in... */
663 //return;
664 GDrawRequestExpose(cv->ruler_w,NULL,false);
665 if (cv->p.pressed)
666 GDrawRequestExpose(cv->v,NULL,false);
667 //#endif // 0
668 }
669
CVMouseUpRuler(CharView * cv,GEvent * event)670 void CVMouseUpRuler(CharView *cv, GEvent *event) {
671 if ( cv->ruler_w!=NULL ) {
672 GRect size;
673
674 last_ruler_offset[1] = last_ruler_offset[0];
675 last_ruler_offset[0].x = cv->info.x-cv->p.cx;
676 last_ruler_offset[0].y = cv->info.y-cv->p.cy;
677
678 /* if we have gone out of bounds abandon the window */
679 GDrawGetSize(cv->v,&size);
680 if ( event->u.mouse.x<0 || event->u.mouse.y<0 || event->u.mouse.x>=size.width || event->u.mouse.y>=size.height ) {
681 GDrawDestroyWindow(cv->ruler_w);
682 cv->ruler_w = NULL;
683 return;
684 }
685
686 if ( !(event->u.mouse.state & ksm_meta) ) {
687 /*cv->autonomous_ruler_w = true;*/
688
689 if ( cv->ruler_linger_w ) {
690 GDrawDestroyWindow(cv->ruler_linger_w);
691 cv->ruler_linger_w = NULL;
692 }
693 if ( cv->num_ruler_intersections>1 ) {
694 RulerLingerPlace(cv,event);
695 }
696 return;
697 }
698
699 GDrawDestroyWindow(cv->ruler_w);
700 cv->ruler_w = NULL;
701 }
702 }
703
704 /* ************************************************************************** */
705
PtInfoText(CharView * cv,int lineno,int active,char * buffer,int blen)706 static char *PtInfoText(CharView *cv, int lineno, int active, char *buffer, int blen) {
707 BasePoint *cp;
708 double t;
709 Spline *s;
710 SplinePoint *sp = cv->p.sp;
711 extern char *coord_sep;
712 double dx, dy, kappa, kappa2;
713 int emsize;
714
715 if ( !cv->p.prevcp && !cv->p.nextcp ) {
716 sp = cv->active_sp;
717 if ( sp==NULL )
718 return( NULL );
719 }
720
721 if ( active==-1 ) {
722 if ( lineno>0 )
723 return( NULL );
724 if ( sp->next==NULL || sp->prev==NULL )
725 return( NULL );
726 kappa = SplineCurvature(sp->next,0);
727 kappa2 = SplineCurvature(sp->prev,1);
728 emsize = cv->b.sc->parent->ascent + cv->b.sc->parent->descent;
729 if ( kappa == CURVATURE_ERROR || kappa2 == CURVATURE_ERROR )
730 strncpy(buffer,_("No curvature info"), blen);
731 else
732 snprintf( buffer, blen, U_("∆Curvature: %g"), (kappa-kappa2)*emsize );
733 return( buffer );
734 }
735
736 if ( (!cv->p.prevcp && active ) || (!cv->p.nextcp && !active)) {
737 cp = &sp->nextcp;
738 t = 0;
739 s = sp->next;
740 } else {
741 cp = &sp->prevcp;
742 t = 1;
743 s = sp->prev;
744 }
745
746 switch( lineno ) {
747 case 0:
748 if ( t==0 )
749 strncpy( buffer, _(" Next CP"), blen);
750 else
751 strncpy( buffer, _(" Prev CP"), blen);
752 break;
753 case 1:
754 snprintf( buffer, blen, "(%g%s%g)", (double) cp->x, coord_sep, (double) cp->y);
755 break;
756 case 2:
757 snprintf( buffer, blen, "∆ (%g%s%g)", (double) (cp->x-sp->me.x), coord_sep, (double) (cp->y-sp->me.y));
758 break;
759 case 3:
760 dx = cp->x - sp->me.x; dy = cp->y - sp->me.y;
761 if ( dx==0 && dy==0 )
762 snprintf( buffer, blen, "%s", _("No Slope") );
763 else if ( dx==0 )
764 snprintf( buffer, blen, "∆y/∆x= ∞" );
765 else
766 snprintf( buffer, blen, "∆y/∆x= %g", dy/dx );
767 break;
768 case 4:
769 dx = cp->x - sp->me.x; dy = cp->y - sp->me.y;
770 snprintf( buffer, blen, "∠ %g°", atan2(dy,dx)*180/FF_PI );
771 break;
772 case 5:
773 if ( s==NULL )
774 return( NULL );
775 kappa = SplineCurvature(s,t);
776 if ( kappa==CURVATURE_ERROR )
777 return( NULL );
778 emsize = cv->b.sc->parent->ascent + cv->b.sc->parent->descent;
779 /* If we normalize by the em-size, the curvature is often more */
780 /* readable */
781 snprintf( buffer, blen, _("Curvature: %g"), kappa*emsize);
782 break;
783 default:
784 return( NULL );
785 }
786 return( buffer );
787 }
788
cpinfo_e_h(GWindow gw,GEvent * event)789 static int cpinfo_e_h(GWindow gw, GEvent *event) {
790 CharView *cv = (CharView *) GDrawGetUserData(gw);
791 char buf[100];
792 int line, which, y;
793
794 switch ( event->type ) {
795 case et_expose:
796 y = cv->ras+1;
797 GDrawSetFont(gw,cv->rfont);
798 for ( which = 1; which>=0; --which ) {
799 for ( line=0; PtInfoText(cv,line,which,buf,sizeof(buf))!=NULL; ++line ) {
800 GDrawDrawText8(gw,2,y,buf,-1,0x000000);
801 y += cv->rfh+1;
802 }
803 GDrawDrawLine(gw,0,y+2-cv->ras,2000,y+2-cv->ras,0x000000);
804 y += 4;
805 }
806 if ( PtInfoText(cv,0,-1,buf,sizeof(buf))!=NULL )
807 GDrawDrawText8(gw,2,y,buf,-1,0x000000);
808 break;
809 }
810 return( true );
811 }
812
CpInfoPlace(CharView * cv,GEvent * event)813 static void CpInfoPlace(CharView *cv, GEvent *event) {
814 CharViewTab* tab = CVGetActiveTab(cv);
815 char buf[100];
816 int line, which;
817 int width, x, y;
818 GRect size;
819 GPoint pt, pt2;
820 int h,w;
821 GWindowAttrs wattrs;
822 GRect pos;
823 FontRequest rq;
824 int as, ds, ld;
825 SplinePoint *sp;
826
827 if ( cv->ruler_w==NULL ) {
828 memset(&wattrs,0,sizeof(wattrs));
829 wattrs.mask = wam_events|wam_cursor|wam_positioned|wam_nodecor|wam_backcol|wam_bordwidth;
830 wattrs.event_masks = (1<<et_expose)|(1<<et_resize)|(1<<et_mousedown);
831 wattrs.cursor = ct_mypointer;
832 wattrs.background_color = 0xe0e0c0;
833 wattrs.nodecoration = 1;
834 wattrs.border_width = 1;
835 pos.x = pos.y = 0; pos.width=pos.height = 20;
836 cv->ruler_w = GWidgetCreateTopWindow(NULL,&pos,cpinfo_e_h,cv,&wattrs);
837
838 if ( rvfont==NULL ) {
839 memset(&rq,0,sizeof(rq));
840 rq.utf8_family_name = FIXED_UI_FAMILIES;
841 rq.point_size = -12;
842 rq.weight = 400;
843 rvfont = GDrawInstanciateFont(cv->ruler_w,&rq);
844 rvfont = GResourceFindFont("CharView.Measure.Font",rvfont);
845 }
846 cv->rfont = rvfont;
847 GDrawWindowFontMetrics(cv->ruler_w,cv->rfont,&as,&ds,&ld);
848 cv->rfh = as+ds; cv->ras = as;
849 } else
850 GDrawRaise(cv->ruler_w);
851
852 GDrawSetFont(cv->ruler_w,cv->rfont);
853 h = 0; width = 0;
854 for ( which = 0; which<2; ++which ) {
855 for ( line=0; PtInfoText(cv,line,which,buf,sizeof(buf))!=NULL; ++line ) {
856 w = GDrawGetText8Width(cv->ruler_w,buf,-1);
857 if ( w>width ) width = w;
858 h += cv->rfh+1;
859 }
860 h += 4;
861 }
862 if ( PtInfoText(cv,0,-1,buf,sizeof(buf))!=NULL ) {
863 w = GDrawGetText8Width(cv->ruler_w,buf,-1);
864 if ( w>width ) width = w;
865 h += cv->rfh+1;
866 }
867
868 GDrawGetSize(GDrawGetRoot(NULL),&size);
869 pt.x = event->u.mouse.x; pt.y = event->u.mouse.y; /* Address of cp */
870 GDrawTranslateCoordinates(cv->v,GDrawGetRoot(NULL),&pt);
871
872 sp = cv->p.sp;
873 if ( !cv->p.prevcp && !cv->p.nextcp )
874 sp = cv->active_sp;
875 if ( sp!=NULL ) {
876 x = tab->xoff + rint(sp->me.x*tab->scale);
877 y = -tab->yoff + cv->height - rint(sp->me.y*tab->scale);
878 if ( x>=0 && y>=0 && x<cv->width && y<cv->height ) {
879 pt2.x = x; pt2.y = y;
880 GDrawTranslateCoordinates(cv->v,GDrawGetRoot(NULL),&pt2);
881 } else
882 sp = NULL;
883 }
884
885 x = pt.x + infowindowdistance;
886 y = pt.y - cv->ras-2;
887 if ( sp!=NULL && x<=pt2.x-4 && x+width>=pt2.x+4 && y<=pt2.y-4 && y+h>=pt2.y+4 )
888 x = pt2.x + 4;
889 if ( x+width > size.width ) {
890 x = pt.x - width-30;
891 if ( sp!=NULL && x<=pt2.x-4 && x+width>=pt2.x+4 && y<=pt2.y-4 && y+h>=pt2.y+4 )
892 x = pt2.x - width - 4;
893 if ( x<0 ) {
894 x = pt.x + 10;
895 y = pt.y - h - infowindowdistance;
896 if ( sp!=NULL && x<=pt2.x-4 && x+width>=pt2.x+4 && y<=pt2.y-4 && y+h>=pt2.y+4 )
897 y = pt2.y - h - 4;
898 if ( y<0 )
899 y = pt.y+infowindowdistance; /* If this doesn't work we have nowhere else to */
900 /* try so don't check */
901 }
902 }
903 if ( y+h > size.height )
904 y = pt.y - h - cv->ras - 10;
905 GDrawMoveResize(cv->ruler_w,x,y,width+4,h+4);
906 }
907
CPStartInfo(CharView * cv,GEvent * event)908 void CPStartInfo(CharView *cv, GEvent *event) {
909 // printf("CPStartInfo() showcp:%d pressed:%d rw:%p\n", cv->showcpinfo, cv->p.pressed, cv->ruler_w );
910
911 if ( !cv->showcpinfo )
912 return;
913 cv->autonomous_ruler_w = false;
914
915 CpInfoPlace(cv,event);
916 GDrawSetVisible(cv->ruler_w,true);
917 }
918
CPUpdateInfo(CharView * cv,GEvent * event)919 void CPUpdateInfo(CharView *cv, GEvent *event) {
920
921 // printf("CPUpdateInfo() showcp:%d pressed:%d rw:%p\n", cv->showcpinfo, cv->p.pressed, cv->ruler_w );
922
923 if ( !cv->showcpinfo )
924 return;
925 if ( !cv->p.pressed ) {
926 if ( cv->ruler_w!=NULL && GDrawIsVisible(cv->ruler_w)) {
927 GDrawDestroyWindow(cv->ruler_w);
928 cv->ruler_w = NULL;
929 }
930 return;
931 }
932 if ( cv->ruler_w==NULL )
933 CPStartInfo(cv,event);
934 else {
935 CpInfoPlace(cv,event);
936 GDrawSync(NULL);
937 GDrawProcessPendingEvents(NULL); /* The resize needs to happen before the expose */
938 if ( !cv->p.pressed ) /* but a mouse up might sneak in... */
939 return;
940 GDrawRequestExpose(cv->ruler_w,NULL,false);
941 }
942 }
943
CPEndInfo(CharView * cv)944 void CPEndInfo(CharView *cv) {
945 if ( cv->ruler_w!=NULL ) {
946 if ( !cv->p.pressed ) {
947 GDrawDestroyWindow(cv->ruler_w);
948 cv->ruler_w = NULL;
949 }
950 }
951 /* TBD, wrong time to kill? */
952 if ( cv->ruler_linger_w!=NULL && cv->b1_tool!=cvt_ruler && cv->b1_tool_old!=cvt_ruler ) {
953 GDrawDestroyWindow(cv->ruler_linger_w);
954 cv->ruler_linger_w = NULL;
955 }
956 }
957
CVRulerExpose(GWindow pixmap,CharView * cv)958 void CVRulerExpose(GWindow pixmap,CharView *cv) {
959 CharViewTab* tab = CVGetActiveTab(cv);
960
961 if ( cv->b1_tool!=cvt_ruler && cv->b1_tool_old!=cvt_ruler ) {
962 cv->num_ruler_intersections = 0;
963 return;
964 }
965
966 if ( cv->num_ruler_intersections >= 2 ) {
967 int x = tab->xoff + rint(cv->ruler_intersections[0].x*tab->scale);
968 int y = -tab->yoff + cv->height - rint(cv->ruler_intersections[0].y*tab->scale);
969 int xend = tab->xoff + rint(cv->ruler_intersections[cv->num_ruler_intersections-1].x*tab->scale);
970 int yend = -tab->yoff + cv->height - rint(cv->ruler_intersections[cv->num_ruler_intersections-1].y*tab->scale);
971 real xdist = fabs(cv->ruler_intersections[0].x - cv->ruler_intersections[cv->num_ruler_intersections-1].x);
972 real ydist = fabs(cv->ruler_intersections[0].y - cv->ruler_intersections[cv->num_ruler_intersections-1].y);
973 int i;
974 int len;
975 int charwidth = 6; /* TBD */
976 Color textcolor = (cv->start_intersection_snapped && cv->end_intersection_snapped) ? measuretoolcanvasnumberssnappedcol : measuretoolcanvasnumberscol;
977 GRect prev_rect;
978
979 if ( measuretoolshowhorizontolvertical ) {
980 char buf[40];
981 unichar_t ubuf[40];
982
983 if ( xdist*tab->scale>10.0 && ydist*tab->scale>10.0 ) {
984
985 GDrawSetFont(pixmap,cv->rfont);
986 len = snprintf(buf,sizeof buf,"%g",xdist);
987 utf82u_strcpy(ubuf,buf);
988 GDrawDrawText(pixmap,(x+xend)/2 - len*charwidth/2,y + (y > yend ? 12 : -5),ubuf,-1,textcolor);
989 GDrawDrawLine(pixmap,x,y,xend,y,measuretoollinecol);
990
991 len = snprintf(buf,sizeof buf,"%g",ydist);
992 utf82u_strcpy(ubuf,buf);
993 GDrawDrawText(pixmap,xend + (x < xend ? charwidth/2 : -(len * charwidth + charwidth/2)),(y+yend)/2,ubuf,-1,textcolor);
994 GDrawDrawLine(pixmap,xend,y,xend,yend,measuretoollinecol);
995 }
996 }
997
998 if ( !cv->p.rubberlining ) {
999 GDrawDrawLine(pixmap,x,y,xend,yend,measuretoollinecol);
1000 }
1001
1002 GDrawSetFont(pixmap,cv->rfont);
1003 for ( i=0 ; i<cv->num_ruler_intersections; ++i ) {
1004 GRect rect;
1005
1006 rect.x = tab->xoff + rint(cv->ruler_intersections[i].x*tab->scale) - 1;
1007 rect.y = -tab->yoff + cv->height - rint(cv->ruler_intersections[i].y*tab->scale) - 1;
1008 rect.width = 3;
1009 rect.height = 3;
1010
1011 GDrawFillElipse(pixmap,&rect,((i==(cv->num_ruler_intersections-1) && cv->info_sp) || (i==0 && cv->p.sp)) ? measuretoolpointsnappedcol : measuretoolpointcol);
1012 if ( i>0 && (cv->num_ruler_intersections<6 || (prev_rect.x + 10)<rect.x || (prev_rect.y + 10)<rect.y || (prev_rect.y - 10)>rect.y) ) {
1013 real xoff = cv->ruler_intersections[i].x - cv->ruler_intersections[i-1].x;
1014 real yoff = cv->ruler_intersections[i].y - cv->ruler_intersections[i-1].y;
1015 real len = sqrt(xoff*xoff+yoff*yoff);
1016 char buf[40];
1017 unichar_t ubuf[40];
1018 int x,y;
1019
1020 x = (prev_rect.x + rect.x)/2;
1021 y = (prev_rect.y + rect.y)/2;
1022
1023 len = snprintf(buf,sizeof buf,"%g",len);
1024 utf82u_strcpy(ubuf,buf);
1025 GDrawDrawText(pixmap,x + (x < xend ? -(len*charwidth) : charwidth/2 ),y + (y < yend ? 12 : -5),ubuf,-1,textcolor);
1026 }
1027 prev_rect = rect;
1028 }
1029 RulerLingerMove(cv); /* in case things are moving or scaling */
1030 }
1031 }
1032