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