1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1999 Alexander Larsson
3  *
4  * Bezier object  Copyright (C) 1999 Lars R. Clausen
5  * Conversion to use BezierConn by James Henstridge.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <assert.h>
27 #include <math.h>
28 
29 #include "intl.h"
30 #include "object.h"
31 #include "bezier_conn.h"
32 #include "connectionpoint.h"
33 #include "diarenderer.h"
34 #include "attributes.h"
35 #include "widgets.h"
36 #include "diamenu.h"
37 #include "message.h"
38 #include "properties.h"
39 #include "create.h"
40 
41 #include "tool-icons.h"
42 
43 #define DEFAULT_WIDTH 0.15
44 
45 typedef struct _Bezierline Bezierline;
46 
47 struct _Bezierline {
48   BezierConn bez;
49 
50   Color line_color;
51   LineStyle line_style;
52   real dashlength;
53   real line_width;
54   Arrow start_arrow, end_arrow;
55   real absolute_start_gap, absolute_end_gap;
56 };
57 
58 
59 static ObjectChange* bezierline_move_handle(Bezierline *bezierline, Handle *handle,
60 					    Point *to, ConnectionPoint *cp,
61 					    HandleMoveReason reason, ModifierKeys modifiers);
62 static ObjectChange* bezierline_move(Bezierline *bezierline, Point *to);
63 static void bezierline_select(Bezierline *bezierline, Point *clicked_point,
64 			      DiaRenderer *interactive_renderer);
65 static void bezierline_draw(Bezierline *bezierline, DiaRenderer *renderer);
66 static DiaObject *bezierline_create(Point *startpoint,
67 				 void *user_data,
68 				 Handle **handle1,
69 				 Handle **handle2);
70 static real bezierline_distance_from(Bezierline *bezierline, Point *point);
71 static void bezierline_update_data(Bezierline *bezierline);
72 static void bezierline_destroy(Bezierline *bezierline);
73 static DiaObject *bezierline_copy(Bezierline *bezierline);
74 
75 static PropDescription *bezierline_describe_props(Bezierline *bezierline);
76 static void bezierline_get_props(Bezierline *bezierline, GPtrArray *props);
77 static void bezierline_set_props(Bezierline *bezierline, GPtrArray *props);
78 
79 static void bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
80 			  const char *filename);
81 static DiaObject *bezierline_load(ObjectNode obj_node, int version,
82 			     const char *filename);
83 static DiaMenu *bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint);
84 
85 static void compute_gap_points(Bezierline *bezierline, Point *gap_points);
86 static real approx_bez_length(BezierConn *bez);
87 static void exchange_bez_gap_points(BezierConn * bez, Point* gap_points);
88 
89 static ObjectTypeOps bezierline_type_ops =
90 {
91   (CreateFunc)bezierline_create,   /* create */
92   (LoadFunc)  bezierline_load,     /* load */
93   (SaveFunc)  bezierline_save,      /* save */
94   (GetDefaultsFunc)   NULL,
95   (ApplyDefaultsFunc) NULL
96 };
97 
98 static DiaObjectType bezierline_type =
99 {
100   "Standard - BezierLine",   /* name */
101   0,                         /* version */
102   (char **) bezierline_icon,      /* pixmap */
103 
104   &bezierline_type_ops,      /* ops */
105   NULL,                      /* pixmap_file */
106   0                          /* default_user_data */
107 };
108 
109 DiaObjectType *_bezierline_type = (DiaObjectType *) &bezierline_type;
110 
111 
112 static ObjectOps bezierline_ops = {
113   (DestroyFunc)         bezierline_destroy,
114   (DrawFunc)            bezierline_draw,
115   (DistanceFunc)        bezierline_distance_from,
116   (SelectFunc)          bezierline_select,
117   (CopyFunc)            bezierline_copy,
118   (MoveFunc)            bezierline_move,
119   (MoveHandleFunc)      bezierline_move_handle,
120   (GetPropertiesFunc)   object_create_props_dialog,
121   (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
122   (ObjectMenuFunc)      bezierline_get_object_menu,
123   (DescribePropsFunc)   bezierline_describe_props,
124   (GetPropsFunc)        bezierline_get_props,
125   (SetPropsFunc)        bezierline_set_props,
126   (TextEditFunc) 0,
127   (ApplyPropertiesListFunc) object_apply_props,
128 };
129 
130 static PropNumData gap_range = { -G_MAXFLOAT, G_MAXFLOAT, 0.1};
131 
132 static PropDescription bezierline_props[] = {
133   BEZCONN_COMMON_PROPERTIES,
134   PROP_STD_LINE_WIDTH,
135   PROP_STD_LINE_COLOUR,
136   PROP_STD_LINE_STYLE,
137   PROP_STD_START_ARROW,
138   PROP_STD_END_ARROW,
139   PROP_FRAME_BEGIN("gaps",0,N_("Line gaps")),
140   { "absolute_start_gap", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
141     N_("Absolute start gap"), NULL, &gap_range },
142   { "absolute_end_gap", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
143     N_("Absolute end gap"), NULL, &gap_range },
144   PROP_FRAME_END("gaps",0),
145   PROP_DESC_END
146 };
147 
148 static PropDescription *
bezierline_describe_props(Bezierline * bezierline)149 bezierline_describe_props(Bezierline *bezierline)
150 {
151   if (bezierline_props[0].quark == 0)
152     prop_desc_list_calculate_quarks(bezierline_props);
153   return bezierline_props;
154 }
155 
156 static PropOffset bezierline_offsets[] = {
157   BEZCONN_COMMON_PROPERTIES_OFFSETS,
158   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Bezierline, line_width) },
159   { "line_colour", PROP_TYPE_COLOUR, offsetof(Bezierline, line_color) },
160   { "line_style", PROP_TYPE_LINESTYLE,
161     offsetof(Bezierline, line_style), offsetof(Bezierline, dashlength) },
162   { "start_arrow", PROP_TYPE_ARROW, offsetof(Bezierline, start_arrow) },
163   { "end_arrow", PROP_TYPE_ARROW, offsetof(Bezierline, end_arrow) },
164   PROP_OFFSET_FRAME_BEGIN("gaps"),
165   { "absolute_start_gap", PROP_TYPE_REAL, offsetof(Bezierline, absolute_start_gap) },
166   { "absolute_end_gap", PROP_TYPE_REAL, offsetof(Bezierline, absolute_end_gap) },
167   PROP_OFFSET_FRAME_END("gaps"),
168   { NULL, 0, 0 }
169 };
170 
171 static void
bezierline_get_props(Bezierline * bezierline,GPtrArray * props)172 bezierline_get_props(Bezierline *bezierline, GPtrArray *props)
173 {
174   object_get_props_from_offsets(&bezierline->bez.object, bezierline_offsets,
175 				props);
176 }
177 
178 static void
bezierline_set_props(Bezierline * bezierline,GPtrArray * props)179 bezierline_set_props(Bezierline *bezierline, GPtrArray *props)
180 {
181   object_set_props_from_offsets(&bezierline->bez.object, bezierline_offsets,
182 				props);
183   bezierline_update_data(bezierline);
184 }
185 
186 static real
bezierline_distance_from(Bezierline * bezierline,Point * point)187 bezierline_distance_from(Bezierline *bezierline, Point *point)
188 {
189   BezierConn *bez = &bezierline->bez;
190   if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
191       connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
192       bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
193     Point gap_points[4];
194     real distance;
195     compute_gap_points(bezierline, gap_points);
196     exchange_bez_gap_points(bez,gap_points);
197     distance = bezierconn_distance_from(bez, point, bezierline->line_width);
198     exchange_bez_gap_points(bez,gap_points);
199     return distance;
200   } else {
201     return bezierconn_distance_from(bez, point, bezierline->line_width);
202   }
203 }
204 
bezierline_closest_segment(Bezierline * bezierline,Point * point)205 static int bezierline_closest_segment(Bezierline *bezierline, Point *point) {
206   BezierConn *bez = &bezierline->bez;
207   return bezierconn_closest_segment(bez, point, bezierline->line_width);
208 }
209 
210 static void
bezierline_select(Bezierline * bezierline,Point * clicked_point,DiaRenderer * interactive_renderer)211 bezierline_select(Bezierline *bezierline, Point *clicked_point,
212 		  DiaRenderer *interactive_renderer)
213 {
214   bezierconn_update_data(&bezierline->bez);
215 }
216 
217 static ObjectChange*
bezierline_move_handle(Bezierline * bezierline,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)218 bezierline_move_handle(Bezierline *bezierline, Handle *handle,
219 		       Point *to, ConnectionPoint *cp,
220 		       HandleMoveReason reason, ModifierKeys modifiers)
221 {
222   assert(bezierline!=NULL);
223   assert(handle!=NULL);
224   assert(to!=NULL);
225 
226   if (reason == HANDLE_MOVE_CREATE || reason == HANDLE_MOVE_CREATE_FINAL) {
227     /* During creation, change the control points */
228     BezierConn *bez = &bezierline->bez;
229     Point dist = bez->points[0].p1;
230 
231     point_sub(&dist, to);
232     dist.y = 0;
233     point_scale(&dist, 0.332);
234 
235     bezierconn_move_handle(bez, handle, to, cp, reason, modifiers);
236 
237     bez->points[1].p1 = bez->points[0].p1;
238     point_sub(&bez->points[1].p1, &dist);
239     bez->points[1].p2 = *to;
240     point_add(&bez->points[1].p2, &dist);
241   } else {
242     bezierconn_move_handle(&bezierline->bez, handle, to, cp, reason, modifiers);
243   }
244 
245   bezierline_update_data(bezierline);
246 
247   return NULL;
248 }
249 
250 
251 static ObjectChange*
bezierline_move(Bezierline * bezierline,Point * to)252 bezierline_move(Bezierline *bezierline, Point *to)
253 {
254   bezierconn_move(&bezierline->bez, to);
255   bezierline_update_data(bezierline);
256 
257   return NULL;
258 }
259 
exchange_bez_gap_points(BezierConn * bez,Point * gap_points)260 static void exchange_bez_gap_points(BezierConn * bez, Point* gap_points)
261 {
262         Point tmp_points[4];
263         tmp_points[0] = bez->points[0].p1;
264         tmp_points[1] = bez->points[1].p1;
265         tmp_points[2] = bez->points[bez->numpoints-1].p2;
266         tmp_points[3] = bez->points[bez->numpoints-1].p3;
267         bez->points[0].p1 = gap_points[0];
268         bez->points[1].p1 = gap_points[1];
269         bez->points[bez->numpoints-1].p2 = gap_points[2];
270         bez->points[bez->numpoints-1].p3 = gap_points[3];
271         gap_points[0] = tmp_points[0];
272         gap_points[1] = tmp_points[1];
273         gap_points[2] = tmp_points[2];
274         gap_points[3] = tmp_points[3];
275 }
approx_bez_length(BezierConn * bez)276 static real approx_bez_length(BezierConn *bez)
277 {
278         /* Approximates the length of the bezier curve
279          * by the length of the polyline joining its points */
280         Point *current, *last, vec;
281         real length = .0;
282         int i;
283         current = &bez->points[0].p1;
284         for (i=1; i < bez->numpoints ; i++){
285             last = current;
286             current = &bez->points[i].p3;
287             point_copy(&vec,last);
288             point_sub(&vec,current);
289             length += point_len(&vec);
290         }
291         return length;
292 }
293 
compute_gap_points(Bezierline * bezierline,Point * gap_points)294 static void compute_gap_points(Bezierline *bezierline, Point *gap_points)
295 {
296         real first_length, last_length, bez_length;
297         BezierConn *bez = &bezierline->bez;
298         Point vec_start, vec_end;
299 
300 
301         gap_points[0] = bez->points[0].p1;
302         gap_points[1] = bez->points[1].p1;
303         gap_points[2] = bez->points[bez->numpoints-1].p2;
304         gap_points[3] = bez->points[bez->numpoints-1].p3;
305 
306         point_copy(&vec_start, &gap_points[1]);
307         point_sub(&vec_start, &gap_points[0]);
308         point_normalize(&vec_start); /* unit vector pointing from first point */
309         point_copy(&vec_end, &gap_points[2]);
310         point_sub(&vec_end, &gap_points[3]);
311         point_normalize(&vec_end); /* unit vector pointing from last point */
312 
313 
314         bez_length = approx_bez_length(bez) ;
315         first_length = distance_point_point(&gap_points[0],&gap_points[1]);
316         last_length = distance_point_point(&gap_points[2],&gap_points[3]);
317 
318         if (connpoint_is_autogap(bez->object.handles[0]->connected_to) &&
319                (bez->object.handles[0])->connected_to != NULL &&
320                (bez->object.handles[0])->connected_to->object != NULL ) {
321             Point end;
322             point_copy(&end, &gap_points[0]);
323             point_add_scaled(&end, &vec_start, bez_length); /* far away on the same slope */
324             end = calculate_object_edge(&gap_points[0], &end,
325                             (bez->object.handles[0])->connected_to->object);
326             point_sub(&end, &gap_points[0]); /* vector from old start to new start */
327             /* move points */
328             point_add(&gap_points[0], &end);
329             point_add(&gap_points[1], &end);
330         }
331 
332         if (connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) &&
333                 (bez->object.handles[3*(bez->numpoints-1)])->connected_to != NULL &&
334                 (bez->object.handles[3*(bez->numpoints-1)])->connected_to->object != NULL) {
335             Point end;
336             point_copy(&end, &gap_points[3]);
337             point_add_scaled(&end, &vec_end, bez_length); /* far away on the same slope */
338             end = calculate_object_edge(&gap_points[3], &end,
339                             (bez->object.handles[3*(bez->numpoints-1)])->connected_to->object);
340             point_sub(&end, &gap_points[3]); /* vector from old end to new end */
341             /* move points */
342             point_add(&gap_points[3], &end);
343             point_add(&gap_points[2], &end);
344         }
345 
346 
347         /* adds the absolute start gap  according to the slope at the first point */
348         point_add_scaled(&gap_points[0], &vec_start, bezierline->absolute_start_gap);
349         point_add_scaled(&gap_points[1], &vec_start, bezierline->absolute_start_gap);
350 
351         /* adds the absolute end gap  according to the slope at the last point */
352         point_add_scaled(&gap_points[2], &vec_end, bezierline->absolute_end_gap);
353         point_add_scaled(&gap_points[3], &vec_end, bezierline->absolute_end_gap);
354 
355 
356 }
357 static void
bezierline_draw(Bezierline * bezierline,DiaRenderer * renderer)358 bezierline_draw(Bezierline *bezierline, DiaRenderer *renderer)
359 {
360   Point gap_points[4]; /* two first and two last bez points */
361 
362   BezierConn *bez = &bezierline->bez;
363   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
364 
365   renderer_ops->set_linewidth(renderer, bezierline->line_width);
366   renderer_ops->set_linestyle(renderer, bezierline->line_style);
367   renderer_ops->set_dashlength(renderer, bezierline->dashlength);
368   renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
369   renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
370 
371   if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
372       connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
373       bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
374 
375     compute_gap_points(bezierline,gap_points);
376     exchange_bez_gap_points(bez,gap_points);
377     renderer_ops->draw_bezier_with_arrows(renderer, bez->points, bez->numpoints,
378 					 bezierline->line_width,
379 					 &bezierline->line_color,
380 					 &bezierline->start_arrow,
381 					 &bezierline->end_arrow);
382     exchange_bez_gap_points(bez,gap_points);
383   } else {
384     renderer_ops->draw_bezier_with_arrows(renderer, bez->points, bez->numpoints,
385 					  bezierline->line_width,
386 					  &bezierline->line_color,
387 					  &bezierline->start_arrow,
388 					  &bezierline->end_arrow);
389   }
390 
391 #if 0
392   renderer_ops->draw_bezier(renderer, bez->points, bez->numpoints,
393 			     &bezierline->line_color);
394 
395   if (bezierline->start_arrow.type != ARROW_NONE) {
396     arrow_draw(renderer, bezierline->start_arrow.type,
397 	       &bez->points[0].p1, &bez->points[1].p1,
398 	       bezierline->start_arrow.length, bezierline->start_arrow.width,
399 	       bezierline->line_width,
400 	       &bezierline->line_color, &color_white);
401   }
402   if (bezierline->end_arrow.type != ARROW_NONE) {
403     arrow_draw(renderer, bezierline->end_arrow.type,
404 	       &bez->points[bez->numpoints-1].p3,
405 	       &bez->points[bez->numpoints-1].p2,
406 	       bezierline->end_arrow.length, bezierline->end_arrow.width,
407 	       bezierline->line_width,
408 	       &bezierline->line_color, &color_white);
409   }
410 #endif
411 
412   /* Only display lines while selected.  Calling dia_object_is_selected()
413    * every time may take a little long.  Some time can be saved by storing
414    * whether the object is currently selected in bezierline_select, and
415    * only checking while selected.  But we'll do that if needed.
416    */
417   if (renderer->is_interactive &&
418       dia_object_is_selected(&bezierline->bez.object)) {
419     bezierconn_draw_control_lines(&bezierline->bez, renderer);
420   }
421 }
422 
423 static DiaObject *
bezierline_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)424 bezierline_create(Point *startpoint,
425 		  void *user_data,
426 		  Handle **handle1,
427 		  Handle **handle2)
428 {
429   Bezierline *bezierline;
430   BezierConn *bez;
431   DiaObject *obj;
432   Point defaultlen = { .3, .3 };
433 
434   bezierline = g_new0(Bezierline, 1);
435   bez = &bezierline->bez;
436   obj = &bez->object;
437 
438   obj->type = &bezierline_type;
439   obj->ops = &bezierline_ops;
440 
441   if (user_data == NULL) {
442     bezierconn_init(bez, 2);
443 
444     bez->points[0].p1 = *startpoint;
445     bez->points[1].p1 = *startpoint;
446     point_add(&bez->points[1].p1, &defaultlen);
447     bez->points[1].p2 = bez->points[1].p1;
448     point_add(&bez->points[1].p2, &defaultlen);
449     bez->points[1].p3 = bez->points[1].p2;
450     point_add(&bez->points[1].p3, &defaultlen);
451   } else {
452     BezierCreateData *bcd = (BezierCreateData*)user_data;
453 
454     bezierconn_init(bez, bcd->num_points);
455     bezierconn_set_points(bez, bcd->num_points, bcd->points);
456   }
457 
458   bezierline->line_width =  attributes_get_default_linewidth();
459   bezierline->line_color = attributes_get_foreground();
460   attributes_get_default_line_style(&bezierline->line_style,
461 				    &bezierline->dashlength);
462   bezierline->start_arrow = attributes_get_default_start_arrow();
463   bezierline->end_arrow = attributes_get_default_end_arrow();
464 
465   *handle1 = bez->object.handles[0];
466   *handle2 = bez->object.handles[3];
467 
468   bezierline_update_data(bezierline);
469 
470   return &bezierline->bez.object;
471 }
472 
473 static void
bezierline_destroy(Bezierline * bezierline)474 bezierline_destroy(Bezierline *bezierline)
475 {
476   bezierconn_destroy(&bezierline->bez);
477 }
478 
479 static DiaObject *
bezierline_copy(Bezierline * bezierline)480 bezierline_copy(Bezierline *bezierline)
481 {
482   Bezierline *newbezierline;
483   BezierConn *bez, *newbez;
484   DiaObject *newobj;
485 
486   bez = &bezierline->bez;
487 
488   newbezierline = g_new0(Bezierline, 1);
489   newbez = &newbezierline->bez;
490   newobj = &bez->object;
491 
492   bezierconn_copy(bez, newbez);
493 
494   newbezierline->line_color = bezierline->line_color;
495   newbezierline->line_width = bezierline->line_width;
496   newbezierline->line_style = bezierline->line_style;
497   newbezierline->dashlength = bezierline->dashlength;
498   newbezierline->start_arrow = bezierline->start_arrow;
499   newbezierline->end_arrow = bezierline->end_arrow;
500   newbezierline->absolute_start_gap = bezierline->absolute_start_gap;
501   newbezierline->absolute_end_gap = bezierline->absolute_end_gap;
502 
503   return &newbezierline->bez.object;
504 }
505 
506 
507 static void
bezierline_update_data(Bezierline * bezierline)508 bezierline_update_data(Bezierline *bezierline)
509 {
510   BezierConn *bez = &bezierline->bez;
511   DiaObject *obj = &bez->object;
512   PolyBBExtras *extra = &bez->extra_spacing;
513 
514   bezierconn_update_data(bez);
515 
516   extra->start_trans = extra->start_long =
517   extra->middle_trans =
518   extra->end_trans = extra->end_long = (bezierline->line_width / 2.0);
519 
520   obj->position = bez->points[0].p1;
521 
522   if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
523       connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
524       bezierline->absolute_start_gap || bezierline->absolute_end_gap ||
525       bezierline->start_arrow.type != ARROW_NONE || bezierline->end_arrow.type != ARROW_NONE) {
526     Point gap_points[4];
527     Rectangle bbox_union = {bez->points[0].p1.x, bez->points[0].p1.y, bez->points[0].p1.x, bez->points[0].p1.y};
528     compute_gap_points(bezierline, gap_points);
529     exchange_bez_gap_points(bez,gap_points);
530     /* further modifying the points data, accounts for corrcet arrow and bezier bounding box */
531     if (bezierline->start_arrow.type != ARROW_NONE) {
532       Rectangle bbox;
533       Point move_arrow, move_line;
534       Point to = bez->points[0].p1, from = bez->points[1].p1;
535 
536       calculate_arrow_point(&bezierline->start_arrow, &to, &from, &move_arrow, &move_line, bezierline->line_width);
537       point_sub(&to, &move_arrow);
538       point_sub(&bez->points[0].p1, &move_line);
539       arrow_bbox (&bezierline->start_arrow, bezierline->line_width, &to, &from, &bbox);
540       rectangle_union (&bbox_union, &bbox);
541     }
542     if (bezierline->end_arrow.type != ARROW_NONE) {
543       Rectangle bbox;
544       Point move_arrow, move_line;
545       int num_points = bez->numpoints;
546       Point to = bez->points[num_points-1].p3, from = bez->points[num_points-1].p2;
547 
548       calculate_arrow_point(&bezierline->end_arrow, &to, &from, &move_arrow, &move_line, bezierline->line_width);
549       point_sub(&to, &move_arrow);
550       point_sub(&bez->points[num_points-1].p3, &move_line);
551       arrow_bbox (&bezierline->end_arrow, bezierline->line_width, &to, &from, &bbox);
552       rectangle_union (&bbox_union, &bbox);
553     }
554     bezierconn_update_boundingbox(bez);
555     rectangle_union (&obj->bounding_box, &bbox_union);
556     exchange_bez_gap_points(bez,gap_points);
557   } else {
558     bezierconn_update_boundingbox(bez);
559   }
560     /* add control points to the bounding box, needed to make them visible when showing all
561       * and to remove traces from them */
562   {
563     int i, num_points = bez->numpoints;
564     obj->enclosing_box = obj->bounding_box;
565     /* starting with the second point, the first one is MOVE_TO */
566     for (i = 1; i < num_points; ++i) {
567       if (bez->points[i].type != BEZ_CURVE_TO)
568         continue;
569       rectangle_add_point(&obj->enclosing_box, &bez->points[i].p1);
570       rectangle_add_point(&obj->enclosing_box, &bez->points[i].p2);
571     }
572   }
573 }
574 
575 static void
bezierline_save(Bezierline * bezierline,ObjectNode obj_node,const char * filename)576 bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
577 	      const char *filename)
578 {
579   if (connpoint_is_autogap(bezierline->bez.object.handles[0]->connected_to) ||
580       connpoint_is_autogap(bezierline->bez.object.handles[3*(bezierline->bez.numpoints-1)]->connected_to) ||
581       bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
582     Point gap_points[4];
583     compute_gap_points(bezierline, gap_points);
584     exchange_bez_gap_points(&bezierline->bez,gap_points);
585     bezierconn_update_boundingbox(&bezierline->bez);
586     exchange_bez_gap_points(&bezierline->bez,gap_points);
587   }
588   bezierconn_save(&bezierline->bez, obj_node);
589 
590   if (!color_equals(&bezierline->line_color, &color_black))
591     data_add_color(new_attribute(obj_node, "line_color"),
592 		   &bezierline->line_color);
593 
594   if (bezierline->line_width != 0.1)
595     data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
596 		  bezierline->line_width);
597 
598   if (bezierline->line_style != LINESTYLE_SOLID)
599     data_add_enum(new_attribute(obj_node, "line_style"),
600 		  bezierline->line_style);
601 
602   if (bezierline->line_style != LINESTYLE_SOLID &&
603       bezierline->dashlength != DEFAULT_LINESTYLE_DASHLEN)
604     data_add_real(new_attribute(obj_node, "dashlength"),
605 		  bezierline->dashlength);
606 
607   if (bezierline->start_arrow.type != ARROW_NONE) {
608     save_arrow(obj_node, &bezierline->start_arrow, "start_arrow",
609 	     "start_arrow_length", "start_arrow_width");
610   }
611 
612   if (bezierline->end_arrow.type != ARROW_NONE) {
613     save_arrow(obj_node, &bezierline->end_arrow, "end_arrow",
614 	     "end_arrow_length", "end_arrow_width");
615   }
616 
617   if (bezierline->absolute_start_gap)
618     data_add_real(new_attribute(obj_node, "absolute_start_gap"),
619                  bezierline->absolute_start_gap);
620   if (bezierline->absolute_end_gap)
621     data_add_real(new_attribute(obj_node, "absolute_end_gap"),
622                  bezierline->absolute_end_gap);
623 }
624 
625 static DiaObject *
bezierline_load(ObjectNode obj_node,int version,const char * filename)626 bezierline_load(ObjectNode obj_node, int version, const char *filename)
627 {
628   Bezierline *bezierline;
629   BezierConn *bez;
630   DiaObject *obj;
631   AttributeNode attr;
632 
633   bezierline = g_new0(Bezierline, 1);
634 
635   bez = &bezierline->bez;
636   obj = &bez->object;
637 
638   obj->type = &bezierline_type;
639   obj->ops = &bezierline_ops;
640 
641   bezierconn_load(bez, obj_node);
642 
643   bezierline->line_color = color_black;
644   attr = object_find_attribute(obj_node, "line_color");
645   if (attr != NULL)
646     data_color(attribute_first_data(attr), &bezierline->line_color);
647 
648   bezierline->line_width = 0.1;
649   attr = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
650   if (attr != NULL)
651     bezierline->line_width = data_real(attribute_first_data(attr));
652 
653   bezierline->line_style = LINESTYLE_SOLID;
654   attr = object_find_attribute(obj_node, "line_style");
655   if (attr != NULL)
656     bezierline->line_style = data_enum(attribute_first_data(attr));
657 
658   bezierline->dashlength = DEFAULT_LINESTYLE_DASHLEN;
659   attr = object_find_attribute(obj_node, "dashlength");
660   if (attr != NULL)
661     bezierline->dashlength = data_real(attribute_first_data(attr));
662 
663   load_arrow(obj_node, &bezierline->start_arrow, "start_arrow",
664 	     "start_arrow_length", "start_arrow_width");
665 
666   load_arrow(obj_node, &bezierline->end_arrow, "end_arrow",
667 	     "end_arrow_length", "end_arrow_width");
668 
669   bezierline->absolute_start_gap = 0.0;
670   attr = object_find_attribute(obj_node, "absolute_start_gap");
671   if (attr != NULL)
672     bezierline->absolute_start_gap =  data_real( attribute_first_data(attr) );
673   bezierline->absolute_end_gap = 0.0;
674   attr = object_find_attribute(obj_node, "absolute_end_gap");
675   if (attr != NULL)
676     bezierline->absolute_end_gap =  data_real( attribute_first_data(attr) );
677 
678   /* if "screws up the bounding box if auto_gap" it must be fixed there
679    * not by copying some meaningless bounding_box before this function call!
680    * But the real fix is in connectionpoint.c(connpoint_is_autogap)
681    */
682   bezierline_update_data(bezierline);
683 
684   return &bezierline->bez.object;
685 }
686 
687 static ObjectChange *
bezierline_add_segment_callback(DiaObject * obj,Point * clicked,gpointer data)688 bezierline_add_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
689 {
690   Bezierline *bezierline = (Bezierline*) obj;
691   int segment;
692   ObjectChange *change;
693 
694   segment = bezierline_closest_segment(bezierline, clicked);
695   change = bezierconn_add_segment(&bezierline->bez, segment, clicked);
696   bezierline_update_data(bezierline);
697   return change;
698 }
699 
700 static ObjectChange *
bezierline_delete_segment_callback(DiaObject * obj,Point * clicked,gpointer data)701 bezierline_delete_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
702 {
703   int seg_nr;
704   Bezierline *bezierline = (Bezierline*) obj;
705   ObjectChange *change;
706 
707   seg_nr = bezierconn_closest_segment(&bezierline->bez, clicked,
708 				      bezierline->line_width);
709 
710   change = bezierconn_remove_segment(&bezierline->bez, seg_nr+1);
711   bezierline_update_data(bezierline);
712   return change;
713 }
714 
715 static ObjectChange *
bezierline_set_corner_type_callback(DiaObject * obj,Point * clicked,gpointer data)716 bezierline_set_corner_type_callback (DiaObject *obj, Point *clicked, gpointer data)
717 {
718   Handle *closest;
719   Bezierline *bezierline = (Bezierline*) obj;
720   ObjectChange *change;
721 
722   closest = bezierconn_closest_major_handle(&bezierline->bez, clicked);
723   change = bezierconn_set_corner_type(&bezierline->bez, closest,
724 				      GPOINTER_TO_INT(data));
725 
726   bezierline_update_data(bezierline);
727   return change;
728 }
729 
730 static DiaMenuItem bezierline_menu_items[] = {
731   { N_("Add segment"), bezierline_add_segment_callback, NULL, 1 },
732   { N_("Delete segment"), bezierline_delete_segment_callback, NULL, 1 },
733   { NULL, NULL, NULL, 1 },
734   { N_("Symmetric control"), bezierline_set_corner_type_callback,
735     GINT_TO_POINTER(BEZ_CORNER_SYMMETRIC), 1 },
736   { N_("Smooth control"), bezierline_set_corner_type_callback,
737     GINT_TO_POINTER(BEZ_CORNER_SMOOTH), 1 },
738   { N_("Cusp control"), bezierline_set_corner_type_callback,
739     GINT_TO_POINTER(BEZ_CORNER_CUSP), 1 },
740 };
741 
742 static DiaMenu bezierline_menu = {
743   "Bezierline",
744   sizeof(bezierline_menu_items)/sizeof(DiaMenuItem),
745   bezierline_menu_items,
746   NULL
747 };
748 
749 static DiaMenu *
bezierline_get_object_menu(Bezierline * bezierline,Point * clickedpoint)750 bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint)
751 {
752   Handle *closest;
753   gboolean closest_is_endpoint;
754   BezCornerType ctype = 42; /* nothing */
755   gint i;
756 
757   closest = bezierconn_closest_major_handle(&bezierline->bez, clickedpoint);
758   if (closest->id == HANDLE_MOVE_STARTPOINT ||
759       closest->id == HANDLE_MOVE_ENDPOINT)
760     closest_is_endpoint = TRUE;
761   else
762     closest_is_endpoint = FALSE;
763 
764   for (i = 0; i < bezierline->bez.numpoints; i++)
765     if (bezierline->bez.object.handles[3*i] == closest) {
766       ctype = bezierline->bez.corner_types[i];
767       break;
768     }
769 
770   /* Set entries sensitive/selected etc here */
771   bezierline_menu_items[0].active = 1;
772   bezierline_menu_items[1].active = bezierline->bez.numpoints > 2;
773   bezierline_menu_items[3].active = !closest_is_endpoint &&
774     (ctype != BEZ_CORNER_SYMMETRIC);
775   bezierline_menu_items[4].active = !closest_is_endpoint &&
776     (ctype != BEZ_CORNER_SMOOTH);
777   bezierline_menu_items[5].active = !closest_is_endpoint &&
778     (ctype != BEZ_CORNER_CUSP);
779   return &bezierline_menu;
780 }
781