1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * This part Copyright (c) 1999-2002 by Alexander Durner
4  *
5  * Any party obtaining a copy of these files is granted, free of charge, a
6  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
7  * nonexclusive right and license to deal in this software and documentation
8  * files (the "Software"), including without limitation the rights to use,
9  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
10  * the Software, and to permit persons who receive copies from any such
11  * party to do so, with the only requirement being that the above copyright
12  * and this permission notice remain intact.
13  */
14 
15 #include "fig.h"
16 #include "resources.h"
17 #include "object.h"
18 #include "mode.h"
19 #include "u_list.h"
20 #include "w_setup.h"
21 #include "w_zoom.h"
22 
23 #include "u_smartsearch.h"
24 
25 #include "u_geom.h"
26 #include "u_markers.h"
27 #include "u_search.h"
28 
29 /* how close to user-selected location? */
30 #define TOLERANCE (zoomscale>1?2:(int)(2/zoomscale))
31 
32 /* `singularities' */
33 #define SING_TOLERANCE (zoomscale>.5?1:(int)(.5/zoomscale))
34 
35 /***************************************************************************/
36 
37 Boolean smart_next_arc_found(int x, int y, int tolerance, int *px, int *py, int shift);
38 Boolean smart_next_ellipse_found(int x, int y, int tolerance, int *px, int *py, int shift);
39 Boolean smart_next_line_found(int x, int y, int tolerance, int *px, int *py, int shift);
40 Boolean smart_next_spline_found(int x, int y, int tolerance, int *px, int *py, int shift);
41 Boolean smart_next_text_found(int x, int y, int tolerance, int *px, int *py, int shift);
42 Boolean smart_next_compound_found(int x, int y, int tolerance, int *px, int *py, int shift);
43 
44 void do_smart_object_search(int x, int y, unsigned int shift);
45 
46 void smart_show_objecthighlight(void);
47 void smart_erase_objecthighlight(void);
48 void smart_toggle_objecthighlight(void);
49 
50 /* exports: */
51 F_point  smart_point1, smart_point2;
52 
53 /* locals: */
54 static void	(*manipulate) ();
55 static void	(*handlerproc_left) ();
56 static void	(*handlerproc_middle) ();
57 static void	(*handlerproc_right) ();
58 static int	type;
59 static long	objectcount;
60 static long	n;
61 static int	csr_x, csr_y;
62 
63 static F_arc   *a;
64 static F_ellipse *e;
65 static F_line  *l;
66 static F_spline *s;
67 static F_text  *t;
68 static F_compound *c;
69 
70 /***************************************************************************/
71 
72 /* functions: */
73 
74 
75 
76 void
init_smart_searchproc_left(void (* handlerproc)())77 init_smart_searchproc_left(void (*handlerproc) (/* ??? */))
78 {
79     handlerproc_left = handlerproc;
80 }
81 
82 void
init_smart_searchproc_middle(void (* handlerproc)())83 init_smart_searchproc_middle(void (*handlerproc) (/* ??? */))
84 {
85     handlerproc_middle = handlerproc;
86 }
87 
88 void
init_smart_searchproc_right(void (* handlerproc)())89 init_smart_searchproc_right(void (*handlerproc) (/* ??? */))
90 {
91     handlerproc_right = handlerproc;
92 }
93 
94 
95 void					/* Shift Key Status from XEvent */
smart_object_search_left(int x,int y,unsigned int shift)96 smart_object_search_left(int x, int y, unsigned int shift)
97 
98 {
99     manipulate = handlerproc_left;
100     do_smart_object_search(x, y, shift);
101 }
102 
103 void					/* Shift Key Status from XEvent */
smart_object_search_middle(int x,int y,unsigned int shift)104 smart_object_search_middle(int x, int y, unsigned int shift)
105 {
106     manipulate = handlerproc_middle;
107     do_smart_object_search(x, y, shift);
108 }
109 
110 void					/* Shift Key Status from XEvent */
smart_object_search_right(int x,int y,unsigned int shift)111 smart_object_search_right(int x, int y, unsigned int shift)
112 {
113     manipulate = handlerproc_right;
114     do_smart_object_search(x, y, shift);
115 }
116 
117 /***************************************************************************/
118 
119 static void
set_smart_points(int x1,int y1,int x2,int y2)120 set_smart_points(int x1, int y1, int x2, int y2)
121 {
122     smart_point1.x = x1;
123     smart_point1.y = y1;
124     smart_point2.x = x2;
125     smart_point2.y = y2;
126 }
127 
128 static void
init_smart_search(void)129 init_smart_search(void)
130 {
131     if (highlighting)
132 	smart_erase_objecthighlight();
133     else {
134 	objectcount = 0;
135 	if (ellipse_in_mask())
136 	    for (e = objects.ellipses; e != NULL; e = e->next)
137 		objectcount++;
138 	if (anyline_in_mask())
139 	    for (l = objects.lines; l != NULL; l = l->next)
140 		if (validline_in_mask(l))
141 		    objectcount++;
142 	if (anyspline_in_mask())
143 	    for (s = objects.splines; s != NULL; s = s->next)
144 		if (validspline_in_mask(s))
145 		    objectcount++;
146 	if (anytext_in_mask())
147 	    for (t = objects.texts; t != NULL; t = t->next)
148 		if (validtext_in_mask(t))
149 		    objectcount++;
150 	if (arc_in_mask())
151 	    for (a = objects.arcs; a != NULL; a = a->next)
152 		objectcount++;
153 	if (compound_in_mask())
154 	    for (c = objects.compounds; c != NULL; c = c->next)
155 		objectcount++;
156 	e = NULL;
157 	type = O_ELLIPSE;
158     }
159 }
160 
161 void					/* Shift Key Status from XEvent */
do_smart_object_search(int x,int y,unsigned int shift)162 do_smart_object_search(int x, int y, unsigned int shift)
163 {
164     int		    px, py;
165     Boolean	    found = False;
166 
167     init_smart_search();
168     for (n = 0; n < objectcount;) {
169 	switch (type) {
170 	case O_ELLIPSE:
171 	    found = smart_next_ellipse_found(x, y, TOLERANCE, &px, &py, shift);
172 	    break;
173 	case O_POLYLINE:
174 	    found = smart_next_line_found(x, y, TOLERANCE, &px, &py, shift);
175 	    break;
176 	case O_SPLINE:
177 	    found = smart_next_spline_found(x, y, TOLERANCE, &px, &py, shift);
178 	    break;
179 	case O_TXT:
180 	    found = smart_next_text_found(x, y, TOLERANCE, &px, &py, shift);
181 	    break;
182 	case O_ARC:
183 	    found = smart_next_arc_found(x, y, TOLERANCE, &px, &py, shift);
184 	    break;
185 	case O_COMPOUND:
186 	    found = smart_next_compound_found(x, y, TOLERANCE, &px, &py, shift);
187 	    break;
188 	}
189 
190 	if (found)
191 	    break;
192 
193 	switch (type) {
194 	case O_ELLIPSE:
195 	    type = O_POLYLINE;
196 	    l = NULL;
197 	    break;
198 	case O_POLYLINE:
199 	    type = O_SPLINE;
200 	    s = NULL;
201 	    break;
202 	case O_SPLINE:
203 	    type = O_TXT;
204 	    t = NULL;
205 	    break;
206 	case O_TXT:
207 	    type = O_ARC;
208 	    a = NULL;
209 	    break;
210 	case O_ARC:
211 	    type = O_COMPOUND;
212 	    c = NULL;
213 	    break;
214 	case O_COMPOUND:
215 	    type = O_ELLIPSE;
216 	    e = NULL;
217 	    break;
218 	}
219     }
220     if (!found) {		/* nothing found */
221         /* dummy values */
222         smart_point1.x = smart_point1.y = 0;
223         smart_point2.x = smart_point2.y = 0;
224 	csr_x = x;
225 	csr_y = y;
226 	type = -1;
227 	smart_show_objecthighlight();
228     } else if (shift) {		/* show selected object */
229 	smart_show_objecthighlight();
230     } else {			/* user selected an object */
231 	smart_erase_objecthighlight();
232 	switch (type) {
233 	case O_ELLIPSE:
234 	    manipulate(e, type, x, y, px, py);
235 	    break;
236 	case O_POLYLINE:
237 	    manipulate(l, type, x, y, px, py);
238 	    break;
239 	case O_SPLINE:
240 	    manipulate(s, type, x, y, px, py);
241 	    break;
242 	case O_TXT:
243 	    manipulate(t, type, x, y, px, py);
244 	    break;
245 	case O_ARC:
246 	    manipulate(a, type, x, y, px, py);
247 	    break;
248 	case O_COMPOUND:
249 	    manipulate(c, type, x, y, px, py);
250 	    break;
251 	}
252     }
253 }
254 
255 /***************************************************************************/
256 
257 Boolean
smart_next_arc_found(int x,int y,int tolerance,int * px,int * py,int shift)258 smart_next_arc_found(int x, int y, int tolerance, int *px, int *py, int shift)
259 {
260    float ax, ay;
261    int x1, y1, x2, y2;
262 
263    if (!arc_in_mask())
264      return 0;
265    if (a == NULL)
266      a = last_arc(objects.arcs);
267    else if (shift)
268      a = prev_arc(objects.arcs, a);
269 
270    for (; a != NULL; a = prev_arc(objects.arcs, a), n++) {
271      if (!close_to_arc(a, x, y, tolerance, &ax, &ay))
272        continue;
273      /* point found */
274      *px = x1 = round(ax);
275      *py = y1 = round(ay);
276      x2 = x1 + round(ay - a->center.y);
277      y2 = y1 - round(ax - a->center.x);
278      set_smart_points(x1, y1, x2, y2);
279      return 1;
280    }
281    return 0;
282 }
283 
284 Boolean
smart_next_ellipse_found(int x,int y,int tolerance,int * px,int * py,int shift)285 smart_next_ellipse_found(int x, int y, int tolerance, int *px, int *py, int shift)
286 {
287    float ex, ey, vx, vy;
288    int x1, y1, x2, y2;
289 
290    if (!ellipse_in_mask())
291         return (0);
292    if (e == NULL)
293 	e = last_ellipse(objects.ellipses);
294    else if (shift)
295 	e = prev_ellipse(objects.ellipses, e);
296    for (; e != NULL; e = prev_ellipse(objects.ellipses, e), n++) {
297       if (!close_to_ellipse(e, x, y, tolerance, &ex, &ey, &vx, &vy))
298         continue;
299       *px = round(ex);
300       *py = round(ey);
301       /* handle special case of very small ellipse */
302       if (fabs(ex - e->center.x) <= SING_TOLERANCE &&
303           fabs(ey - e->center.y) <= SING_TOLERANCE) {
304         x1 = x2 = *px;
305         y1 = y2 = *py;
306       }
307       else {
308         x1 = *px;
309         y1 = *py;
310         x2 = x1 + round(vx);
311         y2 = y1 + round(vy);
312       }
313       set_smart_points(x1, y1, x2, y2);
314       return 1;
315    }
316    return 0;
317 }
318 
319 Boolean
smart_next_line_found(int x,int y,int tolerance,int * px,int * py,int shift)320 smart_next_line_found(int x, int y, int tolerance, int *px, int *py, int shift)
321 {				/* return the pointer to lines object if the
322 				 * search is successful otherwise return
323 				 * NULL.  The value returned via (px, py) is
324 				 * the closest point on the vector to point
325 				 * (x, y)					 */
326 
327     int lx1, ly1, lx2, ly2;
328 
329     if (!anyline_in_mask())
330 	return (0);
331     if (l == NULL)
332 	l = last_line(objects.lines);
333     else if (shift)
334 	l = prev_line(objects.lines, l);
335 
336     for (; l != NULL; l = prev_line(objects.lines, l)) {
337 	if (validline_in_mask(l)) {
338 	    n++;
339             if (close_to_polyline(l, x, y, tolerance, SING_TOLERANCE, px, py,
340                                   &lx1, &ly1, &lx2, &ly2)) {
341               set_smart_points(lx1, ly1, lx2, ly2);
342               return 1;
343 	    }
344 	}
345     }
346     return 0;
347 }
348 
349 Boolean
smart_next_spline_found(int x,int y,int tolerance,int * px,int * py,int shift)350 smart_next_spline_found(int x, int y, int tolerance, int *px, int *py, int shift)
351 /* We call `close_to_spline' which uses HIGH_PRECISION.
352    Think about it.
353    */
354 {
355     int lx1, ly1, lx2, ly2;
356 
357     if (!anyspline_in_mask())
358 	return (0);
359     if (s == NULL)
360 	s = last_spline(objects.splines);
361     else if (shift)
362 	s = prev_spline(objects.splines, s);
363 
364     for (; s != NULL; s = prev_spline(objects.splines, s)) {
365 	if (validspline_in_mask(s)) {
366 	    n++;
367             if (close_to_spline(s, x, y, tolerance, px, py,
368 				&lx1, &ly1, &lx2, &ly2)) {
369               set_smart_points(lx1, ly1, lx2, ly2);
370               return 1;
371 	    }
372 	}
373     }
374     return 0;
375 }
376 
377 /* actually, the following are not very smart */
378 
379 Boolean
smart_next_text_found(int x,int y,int tolerance,int * px,int * py,int shift)380 smart_next_text_found(int x, int y, int tolerance, int *px, int *py, int shift)
381 {
382     int		    dum, tlength;
383 
384     if (!anytext_in_mask())
385 	return (0);
386     if (t == NULL)
387 	t = last_text(objects.texts);
388     else if (shift)
389 	t = prev_text(objects.texts, t);
390 
391     for (; t != NULL; t = prev_text(objects.texts, t))
392 	if (validtext_in_mask(t)) {
393 	    n++;
394 	    if (in_text_bound(t, x, y, &dum, False)) {
395 		*px = x;
396 		*py = y;
397                 tlength = text_length(t);
398                 set_smart_points(t->base_x, t->base_y,
399                                  t->base_x + round(tlength * cos((double)t->angle)),
400                                  t->base_y + round(tlength * sin((double)t->angle)));
401 		return 1;
402 	    }
403 	}
404     return 0;
405 }
406 
407 Boolean
smart_next_compound_found(int x,int y,int tolerance,int * px,int * py,int shift)408 smart_next_compound_found(int x, int y, int tolerance, int *px, int *py, int shift)
409 {
410     float	    tol2;
411 
412     if (!compound_in_mask())
413 	return (0);
414     if (c == NULL)
415 	c = last_compound(objects.compounds);
416     else if (shift)
417 	c = prev_compound(objects.compounds, c);
418 
419     tol2 = tolerance * tolerance;
420 
421     for (; c != NULL; c = prev_compound(objects.compounds, c), n++) {
422 	if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x,
423 			    c->secorner.y, x, y, tolerance, tol2, px, py)) {
424             set_smart_points(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x, c->secorner.y);
425             return 1;
426 	}
427 	else if (close_to_vector(c->secorner.x, c->secorner.y, c->nwcorner.x,
428 				 c->secorner.y, x, y, tolerance, tol2, px, py)) {
429             set_smart_points(c->secorner.x, c->secorner.y, c->nwcorner.x, c->secorner.y);
430 	    return 1;
431 	}
432 	else if (close_to_vector(c->secorner.x, c->secorner.y, c->secorner.x,
433 				 c->nwcorner.y, x, y, tolerance, tol2, px, py)) {
434             set_smart_points(c->secorner.x, c->secorner.y, c->secorner.x, c->nwcorner.y);
435             return 1;
436 	}
437 	else if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->secorner.x,
438 				 c->nwcorner.y, x, y, tolerance, tol2, px, py)) {
439             set_smart_points(c->nwcorner.x, c->nwcorner.y, c->secorner.x, c->nwcorner.y);
440             return 1;
441 	}
442     }
443     return 0;
444 }
445 
smart_show_objecthighlight(void)446 void smart_show_objecthighlight(void)
447 {
448     if (highlighting)
449 	return;
450     highlighting = 1;
451     smart_toggle_objecthighlight();
452 }
453 
smart_erase_objecthighlight(void)454 void smart_erase_objecthighlight(void)
455 {
456     if (!highlighting)
457 	return;
458     highlighting = 0;
459     smart_toggle_objecthighlight();
460     if (type == -1) {
461 	e = NULL;
462 	type = O_ELLIPSE;
463     }
464 }
465 
smart_toggle_objecthighlight(void)466 void smart_toggle_objecthighlight(void)
467 {
468     switch (type) {
469     case O_ELLIPSE:
470 	toggle_ellipsehighlight(e);
471 	break;
472     case O_POLYLINE:
473 	toggle_linehighlight(l);
474 	break;
475     case O_SPLINE:
476 	toggle_splinehighlight(s);
477 	break;
478     case O_TXT:
479 	toggle_texthighlight(t);
480 	break;
481     case O_ARC:
482 	toggle_archighlight(a);
483 	break;
484     case O_COMPOUND:
485 	toggle_compoundhighlight(c);
486 	break;
487     default:
488 	toggle_csrhighlight(csr_x, csr_y);
489     }
490 }
491 
492 void
smart_null_proc(void)493 smart_null_proc(void)
494 {
495     /* almost does nothing */
496     if (highlighting)
497 	smart_erase_objecthighlight();
498 }
499