1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2015 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2016-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 #include "d_line.h"
20 
21 #include <stdlib.h>
22 #include <X11/Xlib.h>
23 
24 #include "resources.h"
25 #include "mode.h"
26 #include "object.h"
27 #include "paintop.h"
28 #include "e_edit.h"
29 #include "u_create.h"
30 #include "u_elastic.h"
31 #include "u_free.h"
32 #include "u_list.h"
33 #include "u_redraw.h"
34 #include "w_canvas.h"
35 #include "w_cursor.h"
36 #include "w_drawprim.h"
37 #include "w_mousefun.h"
38 #include "w_msgpanel.h"
39 
40 
41 Boolean	freehand_line;
42 
43 /* LOCAL */
44 
45 static Boolean	dimension_line;
46 
47 static void	    init_line_drawing(int x, int y, int shift);
48 static void	    init_line_freehand_drawing(int x, int y);
49 
50 /**********************	 polyline and polygon section  **********************/
51 
52 
53 void
line_drawing_selected(void)54 line_drawing_selected(void)
55 {
56     canvas_kbd_proc = null_proc;
57     canvas_locmove_proc = null_proc;
58     canvas_leftbut_proc = init_line_drawing;
59     canvas_middlebut_proc = init_line_freehand_drawing;
60     set_cursor(crosshair_cursor);
61     reset_action_on();
62     if (cur_mode == F_POLYGON) {
63 	set_mousefun("first point", "freehand", "", "", "", "");
64 	min_num_points = 3;
65 	canvas_rightbut_proc = null_proc;
66     } else {
67 	set_mousefun("first point", "freehand", "single point", "dimension line", "", "");
68 	min_num_points = 1;
69 	num_point = 0;
70 	fix_x = fix_y = -1;
71 	canvas_rightbut_proc = create_lineobject;
72     }
73 }
74 
75 static void
init_line_freehand_drawing(int x,int y)76 init_line_freehand_drawing(int x, int y)
77 {
78     freehand_line = True;
79     /* not a dimension line */
80     dimension_line = False;
81     init_trace_drawing(x, y);
82 }
83 
84 static void
init_line_drawing(int x,int y,int shift)85 init_line_drawing(int x, int y, int shift)
86 {
87     freehand_line = False;
88     /* if the user pressed shift then make a dimension line */
89     dimension_line = shift;
90     if (shift) {
91 	min_num_points = 2;
92     }
93     canvas_middlebut_proc = null_proc;
94     init_trace_drawing(x, y);
95 }
96 
97 void
cancel_line_drawing(void)98 cancel_line_drawing(void)
99 {
100     elastic_line();
101     /* erase last lengths if appres.showlengths is true */
102     erase_lengths();
103     cur_x = fix_x;
104     cur_y = fix_y;
105     if (cur_point != first_point)
106 	elastic_moveline(first_point);	/* erase control vector */
107     free_points(first_point);
108     first_point = NULL;
109     return_proc();
110     draw_mousefun_canvas();
111 }
112 
113 void
init_trace_drawing(int x,int y)114 init_trace_drawing(int x, int y)
115 {
116     if ((first_point = create_point()) == NULL)
117 	return;
118 
119     cur_point = first_point;
120     set_action_on();
121     cur_point->x = fix_x = cur_x = x;
122     cur_point->y = fix_y = cur_y = y;
123     cur_point->next = NULL;
124     canvas_leftbut_proc = get_intermediatepoint;
125     if (freehand_line) {
126 	canvas_locmove_proc = freehand_get_intermediatepoint;
127     } else {
128 	/* only two points in a dimension line */
129 	if (dimension_line)
130 	    canvas_leftbut_proc = create_lineobject;
131 	if (latexline_mode || latexarrow_mode) {
132 	    canvas_locmove_proc = latex_line;
133 	} else if (manhattan_mode || mountain_mode) {
134 	    canvas_locmove_proc = constrainedangle_line;
135 	} else {
136 	    canvas_locmove_proc = unconstrained_line;
137 	}
138     }
139     canvas_middlebut_save = create_lineobject;
140     canvas_rightbut_proc = cancel_line_drawing;
141     return_proc = line_drawing_selected;
142     num_point = 1;
143     set_mousefun("next point", "", "cancel", "del point", "", "");
144     if (dimension_line) {
145 	set_mousefun("final point", "", "cancel", "del point", "", "");
146 	canvas_middlebut_proc = null_proc;
147     } else if (num_point >= min_num_points - 1) {
148 	set_mousefun("next point", "final point", "cancel", "del point", "", "");
149 	canvas_middlebut_proc = canvas_middlebut_save;
150     }
151 
152     draw_mousefun_canvas();
153     set_cursor(null_cursor);
154     elastic_line();
155 }
156 
157 /* we have this extra proc to call get_intermediatepoint() because we come
158    here from a canvas_locmove_proc which doesn't have a shift value (its
159    not a keypress event)
160 */
161 
162 void
freehand_get_intermediatepoint(int x,int y)163 freehand_get_intermediatepoint(int x, int y)
164 {
165     /* if shift key is pressed user wants to delete points with
166        left button press, return now */
167     if (shift) {
168 	unconstrained_line(x,y);
169 	return;
170     }
171     get_intermediatepoint(x, y, 0);
172 }
173 
174 void
get_intermediatepoint(int x,int y,int shift)175 get_intermediatepoint(int x, int y, int shift)
176 {
177     /* in freehand mode call unconstrained_line explicitely to move the mouse */
178     if (freehand_line) {
179 	unconstrained_line(x,y);
180 	/* pointer must move by at least freehand_resolution in any direction */
181 	if (abs(fix_x-cur_x) < appres.freehand_resolution &&
182 	   (abs(fix_y-cur_y) < appres.freehand_resolution))
183 		return;
184     } else {
185 	/* otherwise call the (possibly) constrained movement procedure */
186 	(*canvas_locmove_proc) (x, y);
187     }
188 
189     /* don't allow coincident consecutive points */
190     if (fix_x == cur_x && fix_y == cur_y)
191 	return;
192 
193     num_point++;
194     fix_x = cur_x;
195     fix_y = cur_y;
196     elastic_line();
197     if (cur_cursor != null_cursor) {
198 	set_cursor(null_cursor);
199     }
200     if (shift && num_point > 2) {
201 	F_point	*p;
202 
203 	num_point -= 2;
204 	p = prev_point(first_point, cur_point);
205 	p->next = NULL;
206 	/* erase the newest segment */
207 	pw_vector(canvas_win, fix_x, fix_y, cur_point->x, cur_point->y,
208 		  INV_PAINT, 1, RUBBER_LINE, 0.0, DEFAULT);
209 	/* and segment drawn before */
210 	pw_vector(canvas_win, p->x, p->y, cur_point->x, cur_point->y,
211 		  INV_PAINT, 1, RUBBER_LINE, 0.0, DEFAULT);
212 	/* and draw new elastic segment */
213 	pw_vector(canvas_win, fix_x, fix_y, p->x, p->y,
214 		  PAINT, 1, RUBBER_LINE, 0.0, DEFAULT);
215 	fix_x = p->x;
216 	fix_y = p->y;
217 	free_points(cur_point);
218 	cur_point = p;
219     } else {
220 	append_point(fix_x, fix_y, &cur_point);
221     }
222     if (num_point == min_num_points - 1) {
223 	if (freehand_line)
224 	    set_mousefun("", "final point", "cancel", "del point", "", "");
225 	else
226 	    set_mousefun("next point", "final point", "cancel", "del point", "", "");
227 	draw_mousefun_canvas();
228 	canvas_middlebut_proc = canvas_middlebut_save;
229     }
230 }
231 
232 /* come here upon pressing middle button (last point of lineobject) */
233 /* or the second point of a dimension line */
234 
235 void
create_lineobject(int x,int y)236 create_lineobject(int x, int y)
237 {
238     F_line	   *line;
239     F_compound	   *comp;
240     int		    dot;
241 
242     if (num_point == 0) {
243 	if ((first_point = create_point()) == NULL) {
244 	    line_drawing_selected();
245 	    draw_mousefun_canvas();
246 	    return;
247 	}
248 	cur_point = first_point;
249 	first_point->x = fix_x = cur_x = x;
250 	first_point->y = fix_y = cur_y = y;
251 	first_point->next = NULL;
252 	num_point++;
253     } else if (x != fix_x || y != fix_y) {
254 	get_intermediatepoint(x, y, 0);
255     }
256     /* dimension line must have 2 different points */
257     if (dimension_line && first_point->x == x && first_point->y == y)
258 	return;
259 
260     dot = (num_point == 1);
261     elastic_line();
262     /* erase any length info if appres.showlengths is true */
263     erase_lengths();
264     if ((line = create_line()) == NULL) {
265 	line_drawing_selected();
266 	draw_mousefun_canvas();
267 	return;
268     }
269     line->type = T_POLYLINE;
270     line->style = cur_linestyle;
271     line->thickness = cur_linewidth;
272     line->pen_color = cur_pencolor;
273     line->fill_color = cur_fillcolor;
274     line->depth = cur_depth;
275     line->pen_style = -1;
276     line->join_style = cur_joinstyle;
277     line->cap_style = cur_capstyle;
278     line->fill_style = cur_fillstyle;
279     line->style_val = cur_styleval * (cur_linewidth + 1) / 2;
280     line->points = first_point;
281     if (!dot) {
282 	if (cur_mode == F_POLYGON) {	/* close off polygon */
283 	    line->type = T_POLYGON;
284 	    num_point++;
285 	    append_point(first_point->x, first_point->y, &cur_point);
286 	    elastic_line();
287 	    fix_x = first_point->x;
288 	    fix_y = first_point->y;
289 	    elastic_line();	/* fix last elastic line */
290 	} else {		/* polyline; draw any arrows */
291 	    if (autoforwardarrow_mode && !dimension_line)
292 		line->for_arrow = forward_arrow();
293 	    /* arrow will be drawn in draw_line below */
294 	    if (autobackwardarrow_mode && !dimension_line)
295 		line->back_arrow = backward_arrow();
296 	    /* arrow will be drawn in draw_line below */
297 	}
298 	cur_x = fix_x;
299 	cur_y = fix_y;
300 	elastic_moveline(first_point);	/* erase temporary outline */
301     }
302     if (dimension_line) {
303 	comp = create_dimension_line(line, True);
304 	reset_action_on(); /* this signals redisplay_curobj() not to refresh */
305 	/* draw it and anything on top of it */
306 	redisplay_compound(comp);
307     } else {
308 	add_line(line);
309 	reset_action_on(); /* this signals redisplay_curobj() not to refresh */
310 	/* draw it and anything on top of it */
311 	redisplay_line(line);
312     }
313     line_drawing_selected();
314     if (!edit_remember_dimline_mode)
315 	draw_mousefun_canvas();
316 }
317 
318