1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1999 Alexander Larsson
3  *
4  * beziergon.c - a beziergon shape
5  * Copyright (C) 2000 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 "beziershape.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 _BeziergonProperties BeziergonProperties;
46 
47 typedef struct _Beziergon {
48   BezierShape bezier;
49 
50   Color line_color;
51   LineStyle line_style;
52   Color inner_color;
53   gboolean show_background;
54   real dashlength;
55   real line_width;
56 } Beziergon;
57 
58 static struct _BeziergonProperties {
59   gboolean show_background;
60 } default_properties = { TRUE };
61 
62 static ObjectChange* beziergon_move_handle(Beziergon *beziergon, Handle *handle,
63 					   Point *to, ConnectionPoint *cp,
64 					   HandleMoveReason reason, ModifierKeys modifiers);
65 static ObjectChange* beziergon_move(Beziergon *beziergon, Point *to);
66 static void beziergon_select(Beziergon *beziergon, Point *clicked_point,
67 			     DiaRenderer *interactive_renderer);
68 static void beziergon_draw(Beziergon *beziergon, DiaRenderer *renderer);
69 static DiaObject *beziergon_create(Point *startpoint,
70 				void *user_data,
71 				Handle **handle1,
72 				Handle **handle2);
73 static real beziergon_distance_from(Beziergon *beziergon, Point *point);
74 static void beziergon_update_data(Beziergon *beziergon);
75 static void beziergon_destroy(Beziergon *beziergon);
76 static DiaObject *beziergon_copy(Beziergon *beziergon);
77 
78 static PropDescription *beziergon_describe_props(Beziergon *beziergon);
79 static void beziergon_get_props(Beziergon *beziergon, GPtrArray *props);
80 static void beziergon_set_props(Beziergon *beziergon, GPtrArray *props);
81 
82 static void beziergon_save(Beziergon *beziergon, ObjectNode obj_node,
83 			  const char *filename);
84 static DiaObject *beziergon_load(ObjectNode obj_node, int version,
85 			     const char *filename);
86 static DiaMenu *beziergon_get_object_menu(Beziergon *beziergon,
87 					  Point *clickedpoint);
88 
89 static ObjectTypeOps beziergon_type_ops =
90 {
91   (CreateFunc)beziergon_create,   /* create */
92   (LoadFunc)  beziergon_load,     /* load */
93   (SaveFunc)  beziergon_save,      /* save */
94   (GetDefaultsFunc)   NULL,
95   (ApplyDefaultsFunc) NULL
96 };
97 
98 static DiaObjectType beziergon_type =
99 {
100   "Standard - Beziergon",   /* name */
101   0,                         /* version */
102   (char **) beziergon_icon,      /* pixmap */
103 
104   &beziergon_type_ops,      /* ops */
105   NULL,                     /* pixmap_file */
106   0                         /* default_user_data */
107 };
108 
109 DiaObjectType *_beziergon_type = (DiaObjectType *) &beziergon_type;
110 
111 
112 static ObjectOps beziergon_ops = {
113   (DestroyFunc)         beziergon_destroy,
114   (DrawFunc)            beziergon_draw,
115   (DistanceFunc)        beziergon_distance_from,
116   (SelectFunc)          beziergon_select,
117   (CopyFunc)            beziergon_copy,
118   (MoveFunc)            beziergon_move,
119   (MoveHandleFunc)      beziergon_move_handle,
120   (GetPropertiesFunc)   object_create_props_dialog,
121   (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
122   (ObjectMenuFunc)      beziergon_get_object_menu,
123   (DescribePropsFunc)   beziergon_describe_props,
124   (GetPropsFunc)        beziergon_get_props,
125   (SetPropsFunc)        beziergon_set_props,
126   (TextEditFunc) 0,
127   (ApplyPropertiesListFunc) object_apply_props,
128 };
129 
130 static PropDescription beziergon_props[] = {
131   BEZSHAPE_COMMON_PROPERTIES,
132   PROP_STD_LINE_WIDTH,
133   PROP_STD_LINE_COLOUR,
134   PROP_STD_LINE_STYLE,
135   PROP_STD_FILL_COLOUR,
136   PROP_STD_SHOW_BACKGROUND,
137   PROP_DESC_END
138 };
139 
140 static PropDescription *
beziergon_describe_props(Beziergon * beziergon)141 beziergon_describe_props(Beziergon *beziergon)
142 {
143   if (beziergon_props[0].quark == 0)
144     prop_desc_list_calculate_quarks(beziergon_props);
145   return beziergon_props;
146 }
147 
148 static PropOffset beziergon_offsets[] = {
149   BEZSHAPE_COMMON_PROPERTIES_OFFSETS,
150   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Beziergon, line_width) },
151   { "line_colour", PROP_TYPE_COLOUR, offsetof(Beziergon, line_color) },
152   { "line_style", PROP_TYPE_LINESTYLE,
153     offsetof(Beziergon, line_style), offsetof(Beziergon, dashlength) },
154   { "fill_colour", PROP_TYPE_COLOUR, offsetof(Beziergon, inner_color) },
155   { "show_background", PROP_TYPE_BOOL, offsetof(Beziergon, show_background) },
156   { NULL, 0, 0 }
157 };
158 
159 static void
beziergon_get_props(Beziergon * beziergon,GPtrArray * props)160 beziergon_get_props(Beziergon *beziergon, GPtrArray *props)
161 {
162   object_get_props_from_offsets(&beziergon->bezier.object, beziergon_offsets,
163 				props);
164 }
165 
166 static void
beziergon_set_props(Beziergon * beziergon,GPtrArray * props)167 beziergon_set_props(Beziergon *beziergon, GPtrArray *props)
168 {
169   object_set_props_from_offsets(&beziergon->bezier.object, beziergon_offsets,
170 				props);
171   beziergon_update_data(beziergon);
172 }
173 
174 static real
beziergon_distance_from(Beziergon * beziergon,Point * point)175 beziergon_distance_from(Beziergon *beziergon, Point *point)
176 {
177   return beziershape_distance_from(&beziergon->bezier, point,
178 				   beziergon->line_width);
179 }
180 
181 static int
beziergon_closest_segment(Beziergon * beziergon,Point * point)182 beziergon_closest_segment(Beziergon *beziergon, Point *point)
183 {
184   return beziershape_closest_segment(&beziergon->bezier, point,
185 				     beziergon->line_width);
186 }
187 
188 static void
beziergon_select(Beziergon * beziergon,Point * clicked_point,DiaRenderer * interactive_renderer)189 beziergon_select(Beziergon *beziergon, Point *clicked_point,
190 		  DiaRenderer *interactive_renderer)
191 {
192   beziershape_update_data(&beziergon->bezier);
193 }
194 
195 static ObjectChange*
beziergon_move_handle(Beziergon * beziergon,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)196 beziergon_move_handle(Beziergon *beziergon, Handle *handle,
197 		      Point *to, ConnectionPoint *cp,
198 		      HandleMoveReason reason, ModifierKeys modifiers)
199 {
200   assert(beziergon!=NULL);
201   assert(handle!=NULL);
202   assert(to!=NULL);
203 
204   beziershape_move_handle(&beziergon->bezier, handle, to, cp, reason, modifiers);
205   beziergon_update_data(beziergon);
206 
207   return NULL;
208 }
209 
210 
211 static ObjectChange*
beziergon_move(Beziergon * beziergon,Point * to)212 beziergon_move(Beziergon *beziergon, Point *to)
213 {
214   beziershape_move(&beziergon->bezier, to);
215   beziergon_update_data(beziergon);
216 
217   return NULL;
218 }
219 
220 static void
beziergon_draw(Beziergon * beziergon,DiaRenderer * renderer)221 beziergon_draw(Beziergon *beziergon, DiaRenderer *renderer)
222 {
223   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
224 
225   BezierShape *bezier = &beziergon->bezier;
226   BezPoint *points;
227   int n;
228 
229   points = &bezier->points[0];
230   n = bezier->numpoints;
231 
232   renderer_ops->set_linewidth(renderer, beziergon->line_width);
233   renderer_ops->set_linestyle(renderer, beziergon->line_style);
234   renderer_ops->set_dashlength(renderer, beziergon->dashlength);
235   renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
236   renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
237 
238   if (beziergon->show_background)
239     renderer_ops->fill_bezier(renderer, points, n, &beziergon->inner_color);
240 
241   renderer_ops->draw_bezier(renderer, points, n, &beziergon->line_color);
242 
243   /* these lines should only be displayed when object is selected.
244    * Unfortunately the draw function is not aware of the selected
245    * state.  This is a compromise until I fix this properly. */
246   if (renderer->is_interactive &&
247       dia_object_is_selected((DiaObject*)beziergon)) {
248     beziershape_draw_control_lines(&beziergon->bezier, renderer);
249   }
250 }
251 
252 static DiaObject *
beziergon_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)253 beziergon_create(Point *startpoint,
254 		  void *user_data,
255 		  Handle **handle1,
256 		  Handle **handle2)
257 {
258   Beziergon *beziergon;
259   BezierShape *bezier;
260   DiaObject *obj;
261   Point defaultx = { 1.0, 0.0 };
262   Point defaulty = { 0.0, 1.0 };
263 
264   beziergon = g_new0(Beziergon, 1);
265   bezier = &beziergon->bezier;
266   obj = &bezier->object;
267 
268   obj->type = &beziergon_type;
269   obj->ops = &beziergon_ops;
270 
271   if (user_data == NULL) {
272     beziershape_init(bezier, 3);
273 
274     bezier->points[0].p1 = *startpoint;
275     bezier->points[0].p3 = *startpoint;
276     bezier->points[2].p3 = *startpoint;
277 
278     bezier->points[1].p1 = *startpoint;
279     point_add(&bezier->points[1].p1, &defaultx);
280     bezier->points[2].p2 = *startpoint;
281     point_sub(&bezier->points[2].p2, &defaultx);
282 
283     bezier->points[1].p3 = *startpoint;
284     point_add(&bezier->points[1].p3, &defaulty);
285     bezier->points[1].p2 = bezier->points[1].p3;
286     point_add(&bezier->points[1].p2, &defaultx);
287     bezier->points[2].p1 = bezier->points[1].p3;
288     point_sub(&bezier->points[2].p1, &defaultx);
289   } else {
290     BezierCreateData *bcd = (BezierCreateData*)user_data;
291 
292     beziershape_init(bezier, bcd->num_points);
293     beziershape_set_points(bezier, bcd->num_points, bcd->points);
294   }
295   beziergon->line_width =  attributes_get_default_linewidth();
296   beziergon->line_color = attributes_get_foreground();
297   beziergon->inner_color = attributes_get_background();
298   attributes_get_default_line_style(&beziergon->line_style,
299 				    &beziergon->dashlength);
300   beziergon->show_background = default_properties.show_background;
301 
302   beziergon_update_data(beziergon);
303 
304   *handle1 = bezier->object.handles[5];
305   *handle2 = bezier->object.handles[2];
306   return &beziergon->bezier.object;
307 }
308 
309 static void
beziergon_destroy(Beziergon * beziergon)310 beziergon_destroy(Beziergon *beziergon)
311 {
312   beziershape_destroy(&beziergon->bezier);
313 }
314 
315 static DiaObject *
beziergon_copy(Beziergon * beziergon)316 beziergon_copy(Beziergon *beziergon)
317 {
318   Beziergon *newbeziergon;
319   BezierShape *bezier, *newbezier;
320   DiaObject *newobj;
321 
322   bezier = &beziergon->bezier;
323 
324   newbeziergon = g_malloc0(sizeof(Beziergon));
325   newbezier = &newbeziergon->bezier;
326   newobj = &newbezier->object;
327 
328   beziershape_copy(bezier, newbezier);
329 
330   newbeziergon->line_color = beziergon->line_color;
331   newbeziergon->line_width = beziergon->line_width;
332   newbeziergon->line_style = beziergon->line_style;
333   newbeziergon->dashlength = beziergon->dashlength;
334   newbeziergon->inner_color = beziergon->inner_color;
335   newbeziergon->show_background = beziergon->show_background;
336 
337   return &newbeziergon->bezier.object;
338 }
339 
340 static void
beziergon_update_data(Beziergon * beziergon)341 beziergon_update_data(Beziergon *beziergon)
342 {
343   BezierShape *bezier = &beziergon->bezier;
344   DiaObject *obj = &bezier->object;
345   ElementBBExtras *extra = &bezier->extra_spacing;
346 
347   beziershape_update_data(bezier);
348 
349   extra->border_trans = beziergon->line_width / 2.0;
350   beziershape_update_boundingbox(bezier);
351 
352   /* update the enclosing box using the control points */
353   {
354     int i, num_points = bezier->numpoints;
355     obj->enclosing_box = obj->bounding_box;
356     for (i = 0; i < num_points; ++i) {
357       if (bezier->points[i].type != BEZ_CURVE_TO)
358         continue;
359       rectangle_add_point(&obj->enclosing_box, &bezier->points[i].p1);
360       rectangle_add_point(&obj->enclosing_box, &bezier->points[i].p2);
361     }
362   }
363   obj->position = bezier->points[0].p1;
364 }
365 
366 static void
beziergon_save(Beziergon * beziergon,ObjectNode obj_node,const char * filename)367 beziergon_save(Beziergon *beziergon, ObjectNode obj_node,
368 	      const char *filename)
369 {
370   beziershape_save(&beziergon->bezier, obj_node);
371 
372   if (!color_equals(&beziergon->line_color, &color_black))
373     data_add_color(new_attribute(obj_node, "line_color"),
374 		   &beziergon->line_color);
375 
376   if (beziergon->line_width != 0.1)
377     data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
378 		  beziergon->line_width);
379 
380   if (!color_equals(&beziergon->inner_color, &color_white))
381     data_add_color(new_attribute(obj_node, "inner_color"),
382 		   &beziergon->inner_color);
383 
384   data_add_boolean(new_attribute(obj_node, "show_background"),
385 		   beziergon->show_background);
386 
387   if (beziergon->line_style != LINESTYLE_SOLID)
388     data_add_enum(new_attribute(obj_node, "line_style"),
389 		  beziergon->line_style);
390 
391   if (beziergon->line_style != LINESTYLE_SOLID &&
392       beziergon->dashlength != DEFAULT_LINESTYLE_DASHLEN)
393     data_add_real(new_attribute(obj_node, "dashlength"),
394 		  beziergon->dashlength);
395 
396 }
397 
398 static DiaObject *
beziergon_load(ObjectNode obj_node,int version,const char * filename)399 beziergon_load(ObjectNode obj_node, int version, const char *filename)
400 {
401   Beziergon *beziergon;
402   BezierShape *bezier;
403   DiaObject *obj;
404   AttributeNode attr;
405 
406   beziergon = g_malloc0(sizeof(Beziergon));
407 
408   bezier = &beziergon->bezier;
409   obj = &bezier->object;
410 
411   obj->type = &beziergon_type;
412   obj->ops = &beziergon_ops;
413 
414   beziershape_load(bezier, obj_node);
415 
416   beziergon->line_color = color_black;
417   attr = object_find_attribute(obj_node, "line_color");
418   if (attr != NULL)
419     data_color(attribute_first_data(attr), &beziergon->line_color);
420 
421   beziergon->line_width = 0.1;
422   attr = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
423   if (attr != NULL)
424     beziergon->line_width = data_real(attribute_first_data(attr));
425 
426   beziergon->inner_color = color_white;
427   attr = object_find_attribute(obj_node, "inner_color");
428   if (attr != NULL)
429     data_color(attribute_first_data(attr), &beziergon->inner_color);
430 
431   beziergon->show_background = TRUE;
432   attr = object_find_attribute(obj_node, "show_background");
433   if (attr != NULL)
434     beziergon->show_background = data_boolean( attribute_first_data(attr) );
435 
436   beziergon->line_style = LINESTYLE_SOLID;
437   attr = object_find_attribute(obj_node, "line_style");
438   if (attr != NULL)
439     beziergon->line_style = data_enum(attribute_first_data(attr));
440 
441   beziergon->dashlength = DEFAULT_LINESTYLE_DASHLEN;
442   attr = object_find_attribute(obj_node, "dashlength");
443   if (attr != NULL)
444     beziergon->dashlength = data_real(attribute_first_data(attr));
445 
446   beziergon_update_data(beziergon);
447 
448   return &beziergon->bezier.object;
449 }
450 
451 static ObjectChange *
beziergon_add_segment_callback(DiaObject * obj,Point * clicked,gpointer data)452 beziergon_add_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
453 {
454   Beziergon *bezier = (Beziergon*) obj;
455   int segment;
456   ObjectChange *change;
457 
458   segment = beziergon_closest_segment(bezier, clicked);
459   change = beziershape_add_segment(&bezier->bezier, segment, clicked);
460 
461   beziergon_update_data(bezier);
462   return change;
463 }
464 
465 static ObjectChange *
beziergon_delete_segment_callback(DiaObject * obj,Point * clicked,gpointer data)466 beziergon_delete_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
467 {
468   int seg_nr;
469   Beziergon *bezier = (Beziergon*) obj;
470   ObjectChange *change;
471 
472   seg_nr = beziergon_closest_segment(bezier, clicked);
473   change = beziershape_remove_segment(&bezier->bezier, seg_nr);
474 
475   beziergon_update_data(bezier);
476   return change;
477 }
478 
479 static ObjectChange *
beziergon_set_corner_type_callback(DiaObject * obj,Point * clicked,gpointer data)480 beziergon_set_corner_type_callback (DiaObject *obj, Point *clicked, gpointer data)
481 {
482   Handle *closest;
483   Beziergon *beziergon = (Beziergon *) obj;
484   ObjectChange *change;
485 
486   closest = beziershape_closest_major_handle(&beziergon->bezier, clicked);
487   change = beziershape_set_corner_type(&beziergon->bezier, closest,
488 				       GPOINTER_TO_INT(data));
489 
490   beziergon_update_data(beziergon);
491   return change;
492 }
493 
494 static DiaMenuItem beziergon_menu_items[] = {
495   { N_("Add segment"), beziergon_add_segment_callback, NULL, 1 },
496   { N_("Delete segment"), beziergon_delete_segment_callback, NULL, 1 },
497   { NULL, NULL, NULL, 1 },
498   { N_("Symmetric control"), beziergon_set_corner_type_callback,
499     GINT_TO_POINTER(BEZ_CORNER_SYMMETRIC), 1 },
500   { N_("Smooth control"), beziergon_set_corner_type_callback,
501     GINT_TO_POINTER(BEZ_CORNER_SMOOTH), 1 },
502   { N_("Cusp control"), beziergon_set_corner_type_callback,
503     GINT_TO_POINTER(BEZ_CORNER_CUSP), 1 },
504 };
505 
506 static DiaMenu beziergon_menu = {
507   "Beziergon",
508   sizeof(beziergon_menu_items)/sizeof(DiaMenuItem),
509   beziergon_menu_items,
510   NULL
511 };
512 
513 static DiaMenu *
beziergon_get_object_menu(Beziergon * beziergon,Point * clickedpoint)514 beziergon_get_object_menu(Beziergon *beziergon, Point *clickedpoint)
515 {
516   /* Set entries sensitive/selected etc here */
517   beziergon_menu_items[0].active = 1;
518   beziergon_menu_items[1].active = beziergon->bezier.numpoints > 3;
519   return &beziergon_menu;
520 }
521