2001-09-14 Hans Breuer <hans@breuer.org>
[dia.git] / objects / standard / polyline.c
blob3b44b64b6d3dbaf7bf86bf9008449da83aed9659
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <assert.h>
24 #include <gtk/gtk.h>
25 #include <math.h>
27 #include "intl.h"
28 #include "object.h"
29 #include "poly_conn.h"
30 #include "connectionpoint.h"
31 #include "render.h"
32 #include "attributes.h"
33 #include "widgets.h"
34 #include "diamenu.h"
35 #include "message.h"
36 #include "properties.h"
38 #include "create.h"
40 #include "pixmaps/polyline.xpm"
42 #define DEFAULT_WIDTH 0.15
44 typedef struct _Polyline {
45 PolyConn poly;
47 Color line_color;
48 LineStyle line_style;
49 real dashlength;
50 real line_width;
51 Arrow start_arrow, end_arrow;
52 } Polyline;
55 static void polyline_move_handle(Polyline *polyline, Handle *handle,
56 Point *to, HandleMoveReason reason, ModifierKeys modifiers);
57 static void polyline_move(Polyline *polyline, Point *to);
58 static void polyline_select(Polyline *polyline, Point *clicked_point,
59 Renderer *interactive_renderer);
60 static void polyline_draw(Polyline *polyline, Renderer *renderer);
61 static Object *polyline_create(Point *startpoint,
62 void *user_data,
63 Handle **handle1,
64 Handle **handle2);
65 static real polyline_distance_from(Polyline *polyline, Point *point);
66 static void polyline_update_data(Polyline *polyline);
67 static void polyline_destroy(Polyline *polyline);
68 static Object *polyline_copy(Polyline *polyline);
70 static PropDescription *polyline_describe_props(Polyline *polyline);
71 static void polyline_get_props(Polyline *polyline, GPtrArray *props);
72 static void polyline_set_props(Polyline *polyline, GPtrArray *props);
74 static void polyline_save(Polyline *polyline, ObjectNode obj_node,
75 const char *filename);
76 static Object *polyline_load(ObjectNode obj_node, int version,
77 const char *filename);
78 static DiaMenu *polyline_get_object_menu(Polyline *polyline, Point *clickedpoint);
80 static ObjectTypeOps polyline_type_ops =
82 (CreateFunc)polyline_create, /* create */
83 (LoadFunc) polyline_load, /* load */
84 (SaveFunc) polyline_save, /* save */
85 (GetDefaultsFunc) NULL /*polyline_get_defaults*/,
86 (ApplyDefaultsFunc) NULL /*polyline_apply_defaults*/
89 static ObjectType polyline_type =
91 "Standard - PolyLine", /* name */
92 0, /* version */
93 (char **) polyline_xpm, /* pixmap */
95 &polyline_type_ops /* ops */
98 ObjectType *_polyline_type = (ObjectType *) &polyline_type;
101 static ObjectOps polyline_ops = {
102 (DestroyFunc) polyline_destroy,
103 (DrawFunc) polyline_draw,
104 (DistanceFunc) polyline_distance_from,
105 (SelectFunc) polyline_select,
106 (CopyFunc) polyline_copy,
107 (MoveFunc) polyline_move,
108 (MoveHandleFunc) polyline_move_handle,
109 (GetPropertiesFunc) object_create_props_dialog,
110 (ApplyPropertiesFunc) object_apply_props_from_dialog,
111 (ObjectMenuFunc) polyline_get_object_menu,
112 (DescribePropsFunc) polyline_describe_props,
113 (GetPropsFunc) polyline_get_props,
114 (SetPropsFunc) polyline_set_props,
117 static PropDescription polyline_props[] = {
118 OBJECT_COMMON_PROPERTIES,
119 PROP_STD_LINE_WIDTH,
120 PROP_STD_LINE_COLOUR,
121 PROP_STD_LINE_STYLE,
122 PROP_STD_START_ARROW,
123 PROP_STD_END_ARROW,
124 PROP_DESC_END
127 static PropDescription *
128 polyline_describe_props(Polyline *polyline)
130 if (polyline_props[0].quark == 0)
131 prop_desc_list_calculate_quarks(polyline_props);
132 return polyline_props;
135 static PropOffset polyline_offsets[] = {
136 OBJECT_COMMON_PROPERTIES_OFFSETS,
137 { "line_width", PROP_TYPE_REAL, offsetof(Polyline, line_width) },
138 { "line_colour", PROP_TYPE_COLOUR, offsetof(Polyline, line_color) },
139 { "line_style", PROP_TYPE_LINESTYLE,
140 offsetof(Polyline, line_style), offsetof(Polyline, dashlength) },
141 { "start_arrow", PROP_TYPE_ARROW, offsetof(Polyline, start_arrow) },
142 { "end_arrow", PROP_TYPE_ARROW, offsetof(Polyline, end_arrow) },
143 { NULL, 0, 0 }
146 static void
147 polyline_get_props(Polyline *polyline, GPtrArray *props)
149 object_get_props_from_offsets(&polyline->poly.object, polyline_offsets,
150 props);
153 static void
154 polyline_set_props(Polyline *polyline, GPtrArray *props)
156 object_set_props_from_offsets(&polyline->poly.object, polyline_offsets,
157 props);
158 polyline_update_data(polyline);
161 static real
162 polyline_distance_from(Polyline *polyline, Point *point)
164 PolyConn *poly = &polyline->poly;
165 return polyconn_distance_from(poly, point, polyline->line_width);
168 static Handle *polyline_closest_handle(Polyline *polyline, Point *point) {
169 return polyconn_closest_handle(&polyline->poly, point);
172 static int polyline_closest_segment(Polyline *polyline, Point *point) {
173 PolyConn *poly = &polyline->poly;
174 return polyconn_closest_segment(poly, point, polyline->line_width);
177 static void
178 polyline_select(Polyline *polyline, Point *clicked_point,
179 Renderer *interactive_renderer)
181 polyconn_update_data(&polyline->poly);
184 static void
185 polyline_move_handle(Polyline *polyline, Handle *handle,
186 Point *to, HandleMoveReason reason, ModifierKeys modifiers)
188 assert(polyline!=NULL);
189 assert(handle!=NULL);
190 assert(to!=NULL);
192 polyconn_move_handle(&polyline->poly, handle, to, reason);
193 polyline_update_data(polyline);
197 static void
198 polyline_move(Polyline *polyline, Point *to)
200 polyconn_move(&polyline->poly, to);
201 polyline_update_data(polyline);
204 static void
205 polyline_draw(Polyline *polyline, Renderer *renderer)
207 PolyConn *poly = &polyline->poly;
208 Point *points;
209 int n;
211 points = &poly->points[0];
212 n = poly->numpoints;
214 renderer->ops->set_linewidth(renderer, polyline->line_width);
215 renderer->ops->set_linestyle(renderer, polyline->line_style);
216 renderer->ops->set_dashlength(renderer, polyline->dashlength);
217 renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
218 renderer->ops->set_linecaps(renderer, LINECAPS_BUTT);
220 renderer->ops->draw_polyline(renderer, points, n, &polyline->line_color);
222 if (polyline->start_arrow.type != ARROW_NONE) {
223 arrow_draw(renderer, polyline->start_arrow.type,
224 &points[0], &points[1],
225 polyline->start_arrow.length, polyline->start_arrow.width,
226 polyline->line_width,
227 &polyline->line_color, &color_white);
229 if (polyline->end_arrow.type != ARROW_NONE) {
230 arrow_draw(renderer, polyline->end_arrow.type,
231 &points[n-1], &points[n-2],
232 polyline->end_arrow.length, polyline->end_arrow.width,
233 polyline->line_width,
234 &polyline->line_color, &color_white);
238 /** user_data is a struct polyline_create_data, containing an array of
239 points and a count.
240 If user_data is NULL, the startpoint is used and a 1x1 line is created.
241 Otherwise, the startpoint is ignored.
243 static Object *
244 polyline_create(Point *startpoint,
245 void *user_data,
246 Handle **handle1,
247 Handle **handle2)
249 Polyline *polyline;
250 PolyConn *poly;
251 Object *obj;
252 Point defaultlen = { 1.0, 1.0 };
254 /*polyline_init_defaults();*/
255 polyline = g_malloc0(sizeof(Polyline));
256 poly = &polyline->poly;
257 obj = &poly->object;
259 obj->type = &polyline_type;
260 obj->ops = &polyline_ops;
262 if (user_data == NULL) {
263 polyconn_init(poly, 2);
265 poly->points[0] = *startpoint;
266 poly->points[1] = *startpoint;
268 point_add(&poly->points[1], &defaultlen);
270 *handle1 = poly->object.handles[0];
271 *handle2 = poly->object.handles[1];
272 } else {
273 PolylineCreateData *pcd = (PolylineCreateData *)user_data;
275 polyconn_init(poly, pcd->num_points);
277 /* Handles are set up by polyconn_init and polyconn_update_data */
278 polyconn_set_points(poly, pcd->num_points, pcd->points);
280 *handle1 = poly->object.handles[0];
281 *handle2 = poly->object.handles[pcd->num_points-1];
284 polyline_update_data(polyline);
286 polyline->line_width = attributes_get_default_linewidth();
287 polyline->line_color = attributes_get_foreground();
288 attributes_get_default_line_style(&polyline->line_style,
289 &polyline->dashlength);
290 polyline->start_arrow = attributes_get_default_start_arrow();
291 polyline->end_arrow = attributes_get_default_end_arrow();
293 return &polyline->poly.object;
296 static void
297 polyline_destroy(Polyline *polyline)
299 polyconn_destroy(&polyline->poly);
302 static Object *
303 polyline_copy(Polyline *polyline)
305 Polyline *newpolyline;
306 PolyConn *poly, *newpoly;
307 Object *newobj;
309 poly = &polyline->poly;
311 newpolyline = g_malloc0(sizeof(Polyline));
312 newpoly = &newpolyline->poly;
313 newobj = &newpoly->object;
315 polyconn_copy(poly, newpoly);
317 newpolyline->line_color = polyline->line_color;
318 newpolyline->line_width = polyline->line_width;
319 newpolyline->line_style = polyline->line_style;
320 newpolyline->dashlength = polyline->dashlength;
321 newpolyline->start_arrow = polyline->start_arrow;
322 newpolyline->end_arrow = polyline->end_arrow;
324 return &newpolyline->poly.object;
327 static void
328 polyline_update_data(Polyline *polyline)
330 PolyConn *poly = &polyline->poly;
331 Object *obj = &poly->object;
332 PolyBBExtras *extra = &poly->extra_spacing;
334 polyconn_update_data(&polyline->poly);
336 extra->start_trans = (polyline->line_width / 2.0);
337 extra->end_trans = (polyline->line_width / 2.0);
338 extra->middle_trans = (polyline->line_width / 2.0);
340 if (polyline->start_arrow.type != ARROW_NONE)
341 extra->start_trans = MAX(extra->start_trans,polyline->start_arrow.width);
342 if (polyline->end_arrow.type != ARROW_NONE)
343 extra->end_trans = MAX(extra->end_trans,polyline->end_arrow.width);
345 extra->start_long = (polyline->line_width / 2.0);
346 extra->end_long = (polyline->line_width / 2.0);
348 polyconn_update_boundingbox(poly);
350 obj->position = poly->points[0];
353 static void
354 polyline_save(Polyline *polyline, ObjectNode obj_node,
355 const char *filename)
357 polyconn_save(&polyline->poly, obj_node);
359 if (!color_equals(&polyline->line_color, &color_black))
360 data_add_color(new_attribute(obj_node, "line_color"),
361 &polyline->line_color);
363 if (polyline->line_width != 0.1)
364 data_add_real(new_attribute(obj_node, "line_width"),
365 polyline->line_width);
367 if (polyline->line_style != LINESTYLE_SOLID)
368 data_add_enum(new_attribute(obj_node, "line_style"),
369 polyline->line_style);
371 if (polyline->line_style != LINESTYLE_SOLID &&
372 polyline->dashlength != DEFAULT_LINESTYLE_DASHLEN)
373 data_add_real(new_attribute(obj_node, "dashlength"),
374 polyline->dashlength);
376 if (polyline->start_arrow.type != ARROW_NONE) {
377 data_add_enum(new_attribute(obj_node, "start_arrow"),
378 polyline->start_arrow.type);
379 data_add_real(new_attribute(obj_node, "start_arrow_length"),
380 polyline->start_arrow.length);
381 data_add_real(new_attribute(obj_node, "start_arrow_width"),
382 polyline->start_arrow.width);
385 if (polyline->end_arrow.type != ARROW_NONE) {
386 data_add_enum(new_attribute(obj_node, "end_arrow"),
387 polyline->end_arrow.type);
388 data_add_real(new_attribute(obj_node, "end_arrow_length"),
389 polyline->end_arrow.length);
390 data_add_real(new_attribute(obj_node, "end_arrow_width"),
391 polyline->end_arrow.width);
395 static Object *
396 polyline_load(ObjectNode obj_node, int version, const char *filename)
398 Polyline *polyline;
399 PolyConn *poly;
400 Object *obj;
401 AttributeNode attr;
403 polyline = g_malloc0(sizeof(Polyline));
405 poly = &polyline->poly;
406 obj = &poly->object;
408 obj->type = &polyline_type;
409 obj->ops = &polyline_ops;
411 polyconn_load(poly, obj_node);
413 polyline->line_color = color_black;
414 attr = object_find_attribute(obj_node, "line_color");
415 if (attr != NULL)
416 data_color(attribute_first_data(attr), &polyline->line_color);
418 polyline->line_width = 0.1;
419 attr = object_find_attribute(obj_node, "line_width");
420 if (attr != NULL)
421 polyline->line_width = data_real(attribute_first_data(attr));
423 polyline->line_style = LINESTYLE_SOLID;
424 attr = object_find_attribute(obj_node, "line_style");
425 if (attr != NULL)
426 polyline->line_style = data_enum(attribute_first_data(attr));
428 polyline->dashlength = DEFAULT_LINESTYLE_DASHLEN;
429 attr = object_find_attribute(obj_node, "dashlength");
430 if (attr != NULL)
431 polyline->dashlength = data_real(attribute_first_data(attr));
433 polyline->start_arrow.type = ARROW_NONE;
434 polyline->start_arrow.length = 0.8;
435 polyline->start_arrow.width = 0.8;
436 attr = object_find_attribute(obj_node, "start_arrow");
437 if (attr != NULL)
438 polyline->start_arrow.type = data_enum(attribute_first_data(attr));
439 attr = object_find_attribute(obj_node, "start_arrow_length");
440 if (attr != NULL)
441 polyline->start_arrow.length = data_real(attribute_first_data(attr));
442 attr = object_find_attribute(obj_node, "start_arrow_width");
443 if (attr != NULL)
444 polyline->start_arrow.width = data_real(attribute_first_data(attr));
446 polyline->end_arrow.type = ARROW_NONE;
447 polyline->end_arrow.length = 0.8;
448 polyline->end_arrow.width = 0.8;
449 attr = object_find_attribute(obj_node, "end_arrow");
450 if (attr != NULL)
451 polyline->end_arrow.type = data_enum(attribute_first_data(attr));
452 attr = object_find_attribute(obj_node, "end_arrow_length");
453 if (attr != NULL)
454 polyline->end_arrow.length = data_real(attribute_first_data(attr));
455 attr = object_find_attribute(obj_node, "end_arrow_width");
456 if (attr != NULL)
457 polyline->end_arrow.width = data_real(attribute_first_data(attr));
459 polyline_update_data(polyline);
461 return &polyline->poly.object;
464 static ObjectChange *
465 polyline_add_corner_callback (Object *obj, Point *clicked, gpointer data)
467 Polyline *poly = (Polyline*) obj;
468 int segment;
469 ObjectChange *change;
471 segment = polyline_closest_segment(poly, clicked);
472 change = polyconn_add_point(&poly->poly, segment, clicked);
473 polyline_update_data(poly);
474 return change;
477 static ObjectChange *
478 polyline_delete_corner_callback (Object *obj, Point *clicked, gpointer data)
480 Handle *handle;
481 int handle_nr, i;
482 Polyline *poly = (Polyline*) obj;
483 ObjectChange *change;
485 handle = polyline_closest_handle(poly, clicked);
487 for (i = 0; i < obj->num_handles; i++) {
488 if (handle == obj->handles[i]) break;
490 handle_nr = i;
491 change = polyconn_remove_point(&poly->poly, handle_nr);
492 polyline_update_data(poly);
493 return change;
497 static DiaMenuItem polyline_menu_items[] = {
498 { N_("Add Corner"), polyline_add_corner_callback, NULL, 1 },
499 { N_("Delete Corner"), polyline_delete_corner_callback, NULL, 1 },
502 static DiaMenu polyline_menu = {
503 "Polyline",
504 sizeof(polyline_menu_items)/sizeof(DiaMenuItem),
505 polyline_menu_items,
506 NULL
509 static DiaMenu *
510 polyline_get_object_menu(Polyline *polyline, Point *clickedpoint)
512 /* Set entries sensitive/selected etc here */
513 polyline_menu_items[0].active = 1;
514 polyline_menu_items[1].active = polyline->poly.numpoints > 2;
515 return &polyline_menu;