Doc fix, BPMN objects
[dia.git] / objects / custom / custom_object.c
blobfa0f5c23873e79b0d36ec975bd29508394e914e6
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 Alexander Larsson
4 * Custom Objects -- objects defined in XML rather than C.
5 * Copyright (C) 1999 James Henstridge.
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.
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.
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.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <assert.h>
27 #include <gmodule.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
36 #include "intl.h"
37 #include "shape_info.h"
38 #include "object.h"
39 #include "element.h"
40 #include "connectionpoint.h"
41 #include "diarenderer.h"
42 #include "attributes.h"
43 #include "text.h"
44 #include "widgets.h"
45 #include "message.h"
46 #include "sheet.h"
47 #include "properties.h"
48 #include "dia_image.h"
49 #include "custom_object.h"
51 #include "pixmaps/custom.xpm"
53 #define DEFAULT_WIDTH 2.0
54 #define DEFAULT_HEIGHT 2.0
55 #define DEFAULT_BORDER 0.25
57 void custom_object_new(ShapeInfo *info, DiaObjectType **otype);
59 /* used when resizing to decide which side of the shape to expand/shrink */
60 typedef enum {
61 ANCHOR_MIDDLE,
62 ANCHOR_START,
63 ANCHOR_END
64 } AnchorShape;
66 typedef struct _Custom Custom;
68 struct _Custom {
69 Element element;
71 ShapeInfo *info;
72 /* transformation coords */
73 real xscale, yscale;
74 real xoffs, yoffs;
76 ConnectionPoint *connections;
77 real border_width;
78 Color border_color;
79 Color inner_color;
80 gboolean show_background;
81 LineStyle line_style;
82 real dashlength;
84 gboolean flip_h, flip_v;
86 Text *text;
87 TextAttributes attrs;
88 real padding;
91 typedef struct _CustomProperties {
92 Color *fg_color;
93 Color *bg_color;
94 gboolean show_background;
95 real border_width;
97 real padding;
98 DiaFont *font;
99 real font_size;
100 Alignment alignment;
101 Color *font_color;
102 } CustomProperties;
105 static CustomProperties default_properties;
107 static real custom_distance_from(Custom *custom, Point *point);
108 static void custom_select(Custom *custom, Point *clicked_point,
109 DiaRenderer *interactive_renderer);
110 static ObjectChange* custom_move_handle(Custom *custom, Handle *handle,
111 Point *to, ConnectionPoint *cp,
112 HandleMoveReason reason,
113 ModifierKeys modifiers);
114 static ObjectChange* custom_move(Custom *custom, Point *to);
115 static void custom_draw(Custom *custom, DiaRenderer *renderer);
116 static void custom_update_data(Custom *custom, AnchorShape h, AnchorShape v);
117 static void custom_reposition_text(Custom *custom, GraphicElementText *text);
118 static DiaObject *custom_create(Point *startpoint,
119 void *user_data,
120 Handle **handle1,
121 Handle **handle2);
122 static void custom_destroy(Custom *custom);
123 static DiaObject *custom_copy(Custom *custom);
124 static DiaMenu *custom_get_object_menu(Custom *custom, Point *clickedpoint);
126 static PropDescription *custom_describe_props(Custom *custom);
127 static void custom_get_props(Custom *custom, GPtrArray *props);
128 static void custom_set_props(Custom *custom, GPtrArray *props);
130 static DiaObject *custom_load_using_properties(ObjectNode obj_node, int version, const char *filename);
132 static ObjectTypeOps custom_type_ops =
134 (CreateFunc) custom_create,
135 (LoadFunc) custom_load_using_properties,
136 (SaveFunc) object_save_using_properties,
137 (GetDefaultsFunc) NULL,
138 (ApplyDefaultsFunc) NULL
141 /* This looks like it could be static, but it can't because we key
142 on it to determine if an DiaObjectType is a custom/SVG shape */
144 G_MODULE_EXPORT
145 DiaObjectType custom_type =
147 "Custom - Generic", /* name */
148 0, /* version */
149 (char **) custom_xpm, /* pixmap */
151 &custom_type_ops /* ops */
154 static ObjectOps custom_ops = {
155 (DestroyFunc) custom_destroy,
156 (DrawFunc) custom_draw,
157 (DistanceFunc) custom_distance_from,
158 (SelectFunc) custom_select,
159 (CopyFunc) custom_copy,
160 (MoveFunc) custom_move,
161 (MoveHandleFunc) custom_move_handle,
162 (GetPropertiesFunc) object_create_props_dialog,
163 (ApplyPropertiesFunc) object_apply_props_from_dialog,
164 (ObjectMenuFunc) custom_get_object_menu,
165 (DescribePropsFunc) custom_describe_props,
166 (GetPropsFunc) custom_get_props,
167 (SetPropsFunc) custom_set_props
170 static PropDescription custom_props[] = {
171 ELEMENT_COMMON_PROPERTIES,
172 PROP_STD_LINE_WIDTH_OPTIONAL,
173 PROP_STD_LINE_COLOUR_OPTIONAL,
174 PROP_STD_FILL_COLOUR_OPTIONAL,
175 PROP_STD_SHOW_BACKGROUND_OPTIONAL,
176 PROP_STD_LINE_STYLE_OPTIONAL,
177 { "flip_horizontal", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
178 N_("Flip horizontal"), NULL, NULL },
179 { "flip_vertical", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
180 N_("Flip vertical"), NULL, NULL },
181 PROP_DESC_END
184 static PropDescription custom_props_text[] = {
185 ELEMENT_COMMON_PROPERTIES,
186 PROP_STD_LINE_WIDTH_OPTIONAL,
187 PROP_STD_LINE_COLOUR_OPTIONAL,
188 PROP_STD_FILL_COLOUR_OPTIONAL,
189 PROP_STD_SHOW_BACKGROUND_OPTIONAL,
190 PROP_STD_LINE_STYLE_OPTIONAL,
191 PROP_STD_TEXT_ALIGNMENT,
192 PROP_STD_TEXT_FONT,
193 PROP_STD_TEXT_HEIGHT,
194 PROP_STD_TEXT_COLOUR,
195 /* BEWARE: the following makes the whole Text optional during load. Normally this
196 * would leave the object in an inconsitent state but here we have a proper default
197 * initialization even in the load case. See custom_load_using_properties() --hb
199 { "text", PROP_TYPE_TEXT, PROP_FLAG_OPTIONAL,
200 N_("Text"), NULL, NULL },
202 { "flip_horizontal", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
203 N_("Flip horizontal"), NULL, NULL },
204 { "flip_vertical", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
205 N_("Flip vertical"), NULL, NULL },
206 PROP_DESC_END
209 static PropOffset custom_offsets[] = {
210 ELEMENT_COMMON_PROPERTIES_OFFSETS,
211 { "line_width", PROP_TYPE_REAL, offsetof(Custom, border_width) },
212 { "line_colour", PROP_TYPE_COLOUR, offsetof(Custom, border_color) },
213 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Custom, inner_color) },
214 { "show_background", PROP_TYPE_BOOL, offsetof(Custom, show_background) },
215 { "line_style", PROP_TYPE_LINESTYLE,
216 offsetof(Custom, line_style), offsetof(Custom, dashlength) },
217 { "flip_horizontal", PROP_TYPE_BOOL, offsetof(Custom, flip_h) },
218 { "flip_vertical", PROP_TYPE_BOOL, offsetof(Custom, flip_v) },
219 { NULL, 0, 0 }
222 static PropOffset custom_offsets_text[] = {
223 ELEMENT_COMMON_PROPERTIES_OFFSETS,
224 { "line_width", PROP_TYPE_REAL, offsetof(Custom, border_width) },
225 { "line_colour", PROP_TYPE_COLOUR, offsetof(Custom, border_color) },
226 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Custom, inner_color) },
227 { "show_background", PROP_TYPE_BOOL, offsetof(Custom, show_background) },
228 { "line_style", PROP_TYPE_LINESTYLE,
229 offsetof(Custom, line_style), offsetof(Custom, dashlength) },
230 { "flip_horizontal", PROP_TYPE_BOOL, offsetof(Custom, flip_h) },
231 { "flip_vertical", PROP_TYPE_BOOL, offsetof(Custom, flip_v) },
232 {"text",PROP_TYPE_TEXT,offsetof(Custom,text)},
233 {"text_font",PROP_TYPE_FONT,offsetof(Custom,attrs.font)},
234 {"text_height",PROP_TYPE_REAL,offsetof(Custom,attrs.height)},
235 {"text_colour",PROP_TYPE_COLOUR,offsetof(Custom,attrs.color)},
236 {"text_alignment",PROP_TYPE_ENUM,offsetof(Custom,attrs.alignment)},
237 { NULL, 0, 0 }
241 void custom_setup_properties (ShapeInfo *info, xmlNodePtr node)
243 xmlChar *str;
244 xmlNodePtr cur;
245 int n_props, offs = 0;
246 int i;
248 /* count ext_attributes node items */
249 if (node)
251 for (i = 0, cur = node->xmlChildrenNode; cur != NULL; cur = cur->next)
253 if ((!xmlIsBlankNode(cur)) && (cur->type == XML_ELEMENT_NODE))
254 i++;
256 info->n_ext_attr = i;
259 /* create prop tables & initialize constant part */
260 if (info->has_text)
262 n_props = sizeof (custom_props_text) / sizeof (PropDescription);
263 info->props = g_new0 (PropDescription, n_props + info->n_ext_attr);
264 memcpy (info->props, custom_props_text, sizeof (custom_props_text));
265 info->prop_offsets = g_new0 (PropOffset, n_props + info->n_ext_attr);
266 memcpy (info->prop_offsets, custom_offsets_text, sizeof (custom_offsets_text));
268 else
270 n_props = sizeof (custom_props) / sizeof (PropDescription);
271 info->props = g_new0 (PropDescription, n_props + info->n_ext_attr);
272 memcpy (info->props, custom_props, sizeof (custom_props));
273 info->prop_offsets = g_new0 (PropOffset, n_props + info->n_ext_attr);
274 memcpy (info->prop_offsets, custom_offsets, sizeof (custom_offsets));
277 if (node)
279 offs = sizeof (Custom);
280 /* walk ext_attributes node ... */
281 for (i = n_props-1, node = node->xmlChildrenNode; node != NULL; node = node->next)
283 if (xmlIsBlankNode(node))
284 continue;
285 if (node->type != XML_ELEMENT_NODE)
286 continue;
287 if (!strcmp(node->name, "ext_attribute"))
289 gchar *pname, *ptype = 0;
291 str = xmlGetProp(node, "name");
292 if (!str)
293 continue;
294 pname = g_strdup(str);
295 xmlFree(str);
297 str = xmlGetProp(node, "type");
298 if (!str)
300 g_free (pname);
301 continue;
303 ptype = g_strdup(str);
304 xmlFree(str);
306 /* we got here, then fill an entry */
307 info->props[i].name = g_strdup_printf("custom:%s", pname);
308 info->props[i].type = ptype;
309 info->props[i].flags = PROP_FLAG_VISIBLE;
311 str = xmlGetProp(node, "description");
312 if (str)
314 g_free (pname);
315 pname = g_strdup(str);
316 xmlFree(str);
318 info->props[i++].description = pname;
323 prop_desc_list_calculate_quarks (info->props);
325 /* 2nd pass after quarks & ops have been filled in */
326 for (i = n_props-1; i < n_props-1+info->n_ext_attr; i++)
327 if ((info->props[i].ops) && (info->props[i].ops->get_data_size))
328 { /* if prop valid & supported */
329 int size;
330 info->prop_offsets[i].name = info->props[i].name;
331 info->prop_offsets[i].type = info->props[i].type;
332 info->prop_offsets[i].offset = offs;
333 /* FIXME:
334 custom_object.c:328: warning: passing arg 1 of pointer to function
335 from incompatible pointer type
336 We don't have a Property* here so there is not much we can do about.
337 Maybe it even works cause the sizeof() in *_get_data_size can be
338 calculated at compile time. Anyway, a mess ;) --hb
340 size = info->props[i].ops->get_data_size (&info->props[i]);
341 info->ext_attr_size += size;
342 offs += size;
344 else
346 /* hope this is enough to have this prop ignored */
347 info->props[i].flags = PROP_FLAG_DONT_SAVE | PROP_FLAG_OPTIONAL;
351 static PropDescription *
352 custom_describe_props(Custom *custom)
354 return custom->info->props;
357 static void
358 custom_get_props(Custom *custom, GPtrArray *props)
360 if (custom->info->has_text)
361 text_get_attributes(custom->text,&custom->attrs);
362 object_get_props_from_offsets(&custom->element.object,
363 custom->info->prop_offsets,props);
366 static void
367 custom_set_props(Custom *custom, GPtrArray *props)
369 object_set_props_from_offsets(&custom->element.object,
370 custom->info->prop_offsets,props);
371 if (custom->info->has_text)
372 apply_textattr_properties(props,custom->text,"text",&custom->attrs);
373 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
376 static void
377 init_default_values(void) {
378 static int defaults_initialized = 0;
380 if (!defaults_initialized) {
381 default_properties.show_background = 1;
382 default_properties.padding = 0.5 * M_SQRT1_2;
383 default_properties.alignment = ALIGN_CENTER;
384 defaults_initialized = 1;
389 static void
390 transform_coord(Custom *custom, const Point *p1, Point *out)
392 out->x = p1->x * custom->xscale + custom->xoffs;
393 out->y = p1->y * custom->yscale + custom->yoffs;
396 static void
397 transform_rect(Custom *custom, const Rectangle *r1, Rectangle *out)
399 real coord;
400 out->left = r1->left * custom->xscale + custom->xoffs;
401 out->right = r1->right * custom->xscale + custom->xoffs;
402 out->top = r1->top * custom->yscale + custom->yoffs;
403 out->bottom = r1->bottom * custom->yscale + custom->yoffs;
405 if (out->left > out->right) {
406 coord = out->left;
407 out->left = out->right;
408 out->right = coord;
410 if (out->top > out->bottom) {
411 coord = out->top;
412 out->top = out->bottom;
413 out->bottom = coord;
417 static real
418 custom_distance_from(Custom *custom, Point *point)
420 static GArray *arr = NULL, *barr = NULL;
421 Point p1, p2;
422 Rectangle rect;
423 gint i;
424 GList *tmp;
425 real min_dist = G_MAXFLOAT, dist = G_MAXFLOAT;
427 if (!arr)
428 arr = g_array_new(FALSE, FALSE, sizeof(Point));
429 if (!barr)
430 barr = g_array_new(FALSE, FALSE, sizeof(BezPoint));
432 for (tmp = custom->info->display_list; tmp != NULL; tmp = tmp->next) {
433 GraphicElement *el = tmp->data;
434 real line_width = el->any.s.line_width * custom->border_width;
436 switch (el->type) {
437 case GE_LINE:
438 transform_coord(custom, &el->line.p1, &p1);
439 transform_coord(custom, &el->line.p2, &p2);
440 dist = distance_line_point(&p1, &p2, line_width, point);
441 break;
442 case GE_POLYLINE:
443 transform_coord(custom, &el->polyline.points[0], &p1);
444 dist = G_MAXFLOAT;
445 for (i = 1; i < el->polyline.npoints; i++) {
446 real seg_dist;
448 transform_coord(custom, &el->polyline.points[i], &p2);
449 seg_dist = distance_line_point(&p1, &p2, line_width, point);
450 p1 = p2;
451 dist = MIN(dist, seg_dist);
452 if (dist == 0.0)
453 break;
455 break;
456 case GE_POLYGON:
457 g_array_set_size(arr, el->polygon.npoints);
458 for (i = 0; i < el->polygon.npoints; i++)
459 transform_coord(custom, &el->polygon.points[i],
460 &g_array_index(arr, Point, i));
461 dist = distance_polygon_point((Point *)arr->data, el->polygon.npoints,
462 line_width, point);
463 break;
464 case GE_RECT:
465 transform_coord(custom, &el->rect.corner1, &p1);
466 transform_coord(custom, &el->rect.corner2, &p2);
467 if (p1.x < p2.x) {
468 rect.left = p1.x - line_width/2; rect.right = p2.x + line_width/2;
469 } else {
470 rect.left = p2.x - line_width/2; rect.right = p1.x + line_width/2;
472 if (p1.y < p2.y) {
473 rect.top = p1.y - line_width/2; rect.bottom = p2.y + line_width/2;
474 } else {
475 rect.top = p2.y - line_width/2; rect.bottom = p1.y + line_width/2;
477 dist = distance_rectangle_point(&rect, point);
478 break;
479 case GE_IMAGE:
480 p2.x = el->image.topleft.x + el->image.width;
481 p2.y = el->image.topleft.y + el->image.height;
482 transform_coord(custom, &el->image.topleft, &p1);
483 transform_coord(custom, &p2, &p2);
485 rect.left = p1.x;
486 rect.top = p1.y;
487 rect.right = p2.x;
488 rect.bottom = p2.y;
489 dist = distance_rectangle_point(&rect, point);
490 break;
491 case GE_TEXT:
492 custom_reposition_text(custom, &el->text);
493 dist = text_distance_from(el->text.object, point);
494 text_set_position(el->text.object, &el->text.anchor);
495 break;
496 case GE_ELLIPSE:
497 transform_coord(custom, &el->ellipse.center, &p1);
498 dist = distance_ellipse_point(&p1,
499 el->ellipse.width * fabs(custom->xscale),
500 el->ellipse.height * fabs(custom->yscale),
501 line_width, point);
502 break;
503 case GE_PATH:
504 g_array_set_size(barr, el->path.npoints);
505 for (i = 0; i < el->path.npoints; i++)
506 switch (g_array_index(barr,BezPoint,i).type=el->path.points[i].type) {
507 case BEZ_CURVE_TO:
508 transform_coord(custom, &el->path.points[i].p3,
509 &g_array_index(barr, BezPoint, i).p3);
510 transform_coord(custom, &el->path.points[i].p2,
511 &g_array_index(barr, BezPoint, i).p2);
512 case BEZ_MOVE_TO:
513 case BEZ_LINE_TO:
514 transform_coord(custom, &el->path.points[i].p1,
515 &g_array_index(barr, BezPoint, i).p1);
517 dist = distance_bez_line_point((BezPoint *)barr->data, el->path.npoints,
518 line_width, point);
519 break;
520 case GE_SHAPE:
521 g_array_set_size(barr, el->path.npoints);
522 for (i = 0; i < el->path.npoints; i++)
523 switch (g_array_index(barr,BezPoint,i).type=el->path.points[i].type) {
524 case BEZ_CURVE_TO:
525 transform_coord(custom, &el->path.points[i].p3,
526 &g_array_index(barr, BezPoint, i).p3);
527 transform_coord(custom, &el->path.points[i].p2,
528 &g_array_index(barr, BezPoint, i).p2);
529 case BEZ_MOVE_TO:
530 case BEZ_LINE_TO:
531 transform_coord(custom, &el->path.points[i].p1,
532 &g_array_index(barr, BezPoint, i).p1);
534 dist = distance_bez_shape_point((BezPoint *)barr->data, el->path.npoints,
535 line_width, point);
536 break;
538 min_dist = MIN(min_dist, dist);
539 if (min_dist == 0.0)
540 break;
542 if (custom->info->has_text && min_dist != 0.0) {
543 dist = text_distance_from(custom->text, point);
544 min_dist = MIN(min_dist, dist);
546 return min_dist;
549 static void
550 custom_select(Custom *custom, Point *clicked_point,
551 DiaRenderer *interactive_renderer)
553 if (custom->info->has_text) {
554 text_set_cursor(custom->text, clicked_point, interactive_renderer);
555 text_grab_focus(custom->text, &custom->element.object);
558 element_update_handles(&custom->element);
561 static ObjectChange*
562 custom_move_handle(Custom *custom, Handle *handle,
563 Point *to, ConnectionPoint *cp,
564 HandleMoveReason reason, ModifierKeys modifiers)
566 AnchorShape horiz = ANCHOR_MIDDLE, vert = ANCHOR_MIDDLE;
568 assert(custom!=NULL);
569 assert(handle!=NULL);
570 assert(to!=NULL);
572 element_move_handle(&custom->element, handle->id, to, cp, reason, modifiers);
574 switch (handle->id) {
575 case HANDLE_RESIZE_NW:
576 horiz = ANCHOR_END; vert = ANCHOR_END; break;
577 case HANDLE_RESIZE_N:
578 vert = ANCHOR_END; break;
579 case HANDLE_RESIZE_NE:
580 horiz = ANCHOR_START; vert = ANCHOR_END; break;
581 case HANDLE_RESIZE_E:
582 horiz = ANCHOR_START; break;
583 case HANDLE_RESIZE_SE:
584 horiz = ANCHOR_START; vert = ANCHOR_START; break;
585 case HANDLE_RESIZE_S:
586 vert = ANCHOR_START; break;
587 case HANDLE_RESIZE_SW:
588 horiz = ANCHOR_END; vert = ANCHOR_START; break;
589 case HANDLE_RESIZE_W:
590 horiz = ANCHOR_END; break;
591 default:
592 break;
594 custom_update_data(custom, horiz, vert);
596 return NULL;
599 static ObjectChange*
600 custom_move(Custom *custom, Point *to)
602 custom->element.corner = *to;
604 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
606 return NULL;
609 static void
610 get_colour(Custom *custom, Color *colour, gint32 c)
612 switch (c) {
613 case DIA_SVG_COLOUR_NONE:
614 break;
615 case DIA_SVG_COLOUR_FOREGROUND:
616 *colour = custom->border_color;
617 break;
618 case DIA_SVG_COLOUR_BACKGROUND:
619 *colour = custom->inner_color;
620 break;
621 case DIA_SVG_COLOUR_TEXT:
622 *colour = custom->text->color;
623 break;
624 default:
625 colour->red = ((c & 0xff0000) >> 16) / 255.0;
626 colour->green = ((c & 0x00ff00) >> 8) / 255.0;
627 colour->blue = (c & 0x0000ff) / 255.0;
628 break;
632 static void
633 custom_draw(Custom *custom, DiaRenderer *renderer)
635 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
636 static GArray *arr = NULL, *barr = NULL;
637 Point p1, p2;
638 real coord;
639 int i;
640 GList *tmp;
641 Element *elem;
642 real cur_line = 1.0, cur_dash = 1.0;
643 LineCaps cur_caps = LINECAPS_BUTT;
644 LineJoin cur_join = LINEJOIN_MITER;
645 LineStyle cur_style = custom->line_style;
647 assert(custom != NULL);
648 assert(renderer != NULL);
650 if (!arr)
651 arr = g_array_new(FALSE, FALSE, sizeof(Point));
652 if (!barr)
653 barr = g_array_new(FALSE, FALSE, sizeof(BezPoint));
655 elem = &custom->element;
657 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
658 renderer_ops->set_linewidth(renderer, custom->border_width);
659 renderer_ops->set_linestyle(renderer, cur_style);
660 renderer_ops->set_dashlength(renderer, custom->dashlength);
661 renderer_ops->set_linecaps(renderer, cur_caps);
662 renderer_ops->set_linejoin(renderer, cur_join);
664 for (tmp = custom->info->display_list; tmp; tmp = tmp->next) {
665 GraphicElement *el = tmp->data;
666 Color fg, bg;
668 if (el->any.s.line_width != cur_line) {
669 cur_line = el->any.s.line_width;
670 renderer_ops->set_linewidth(renderer,
671 custom->border_width*cur_line);
673 if ((el->any.s.linecap == DIA_SVG_LINECAPS_DEFAULT && cur_caps != LINECAPS_BUTT) ||
674 el->any.s.linecap != cur_caps) {
675 cur_caps = (el->any.s.linecap!=DIA_SVG_LINECAPS_DEFAULT) ?
676 el->any.s.linecap : LINECAPS_BUTT;
677 renderer_ops->set_linecaps(renderer, cur_caps);
679 if ((el->any.s.linejoin==DIA_SVG_LINEJOIN_DEFAULT && cur_join!=LINEJOIN_MITER) ||
680 el->any.s.linejoin != cur_join) {
681 cur_join = (el->any.s.linejoin!=DIA_SVG_LINEJOIN_DEFAULT) ?
682 el->any.s.linejoin : LINEJOIN_MITER;
683 renderer_ops->set_linejoin(renderer, cur_join);
685 if ((el->any.s.linestyle == DIA_SVG_LINESTYLE_DEFAULT &&
686 cur_style != custom->line_style) ||
687 el->any.s.linestyle != cur_style) {
688 cur_style = (el->any.s.linestyle!=DIA_SVG_LINESTYLE_DEFAULT) ?
689 el->any.s.linestyle : custom->line_style;
690 renderer_ops->set_linestyle(renderer, cur_style);
692 if (el->any.s.dashlength != cur_dash) {
693 cur_dash = el->any.s.dashlength;
694 renderer_ops->set_dashlength(renderer,
695 custom->dashlength*cur_dash);
698 cur_line = el->any.s.line_width;
699 get_colour(custom, &fg, el->any.s.stroke);
700 get_colour(custom, &bg, el->any.s.fill);
701 switch (el->type) {
702 case GE_LINE:
703 transform_coord(custom, &el->line.p1, &p1);
704 transform_coord(custom, &el->line.p2, &p2);
705 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
706 renderer_ops->draw_line(renderer, &p1, &p2, &fg);
707 break;
708 case GE_POLYLINE:
709 g_array_set_size(arr, el->polyline.npoints);
710 for (i = 0; i < el->polyline.npoints; i++)
711 transform_coord(custom, &el->polyline.points[i],
712 &g_array_index(arr, Point, i));
713 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
714 renderer_ops->draw_polyline(renderer,
715 (Point *)arr->data, el->polyline.npoints,
716 &fg);
717 break;
718 case GE_POLYGON:
719 g_array_set_size(arr, el->polygon.npoints);
720 for (i = 0; i < el->polygon.npoints; i++)
721 transform_coord(custom, &el->polygon.points[i],
722 &g_array_index(arr, Point, i));
723 if (custom->show_background && el->any.s.fill != DIA_SVG_COLOUR_NONE)
724 renderer_ops->fill_polygon(renderer,
725 (Point *)arr->data, el->polygon.npoints,
726 &bg);
727 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
728 renderer_ops->draw_polygon(renderer,
729 (Point *)arr->data, el->polygon.npoints,
730 &fg);
731 break;
732 case GE_RECT:
733 transform_coord(custom, &el->rect.corner1, &p1);
734 transform_coord(custom, &el->rect.corner2, &p2);
735 if (p1.x > p2.x) {
736 coord = p1.x;
737 p1.x = p2.x;
738 p2.x = coord;
740 if (p1.y > p2.y) {
741 coord = p1.y;
742 p1.y = p2.y;
743 p2.y = coord;
745 if (custom->show_background && el->any.s.fill != DIA_SVG_COLOUR_NONE)
746 renderer_ops->fill_rect(renderer, &p1, &p2, &bg);
747 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
748 renderer_ops->draw_rect(renderer, &p1, &p2, &fg);
749 break;
750 case GE_TEXT:
751 custom_reposition_text(custom, &el->text);
752 text_draw(el->text.object, renderer);
753 text_set_position(el->text.object, &el->text.anchor);
754 break;
755 case GE_ELLIPSE:
756 transform_coord(custom, &el->ellipse.center, &p1);
757 if (custom->show_background && el->any.s.fill != DIA_SVG_COLOUR_NONE)
758 renderer_ops->fill_ellipse(renderer, &p1,
759 el->ellipse.width * fabs(custom->xscale),
760 el->ellipse.height * fabs(custom->yscale),
761 &bg);
762 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
763 renderer_ops->draw_ellipse(renderer, &p1,
764 el->ellipse.width * fabs(custom->xscale),
765 el->ellipse.height * fabs(custom->yscale),
766 &fg);
767 break;
768 case GE_IMAGE:
769 transform_coord(custom, &el->image.topleft, &p1);
770 renderer_ops->draw_image(renderer, &p1,
771 el->image.width * fabs(custom->xscale),
772 el->image.height * fabs(custom->yscale),
773 el->image.image);
774 break;
775 case GE_PATH:
776 g_array_set_size(barr, el->path.npoints);
777 for (i = 0; i < el->path.npoints; i++)
778 switch (g_array_index(barr,BezPoint,i).type=el->path.points[i].type) {
779 case BEZ_CURVE_TO:
780 transform_coord(custom, &el->path.points[i].p3,
781 &g_array_index(barr, BezPoint, i).p3);
782 transform_coord(custom, &el->path.points[i].p2,
783 &g_array_index(barr, BezPoint, i).p2);
784 case BEZ_MOVE_TO:
785 case BEZ_LINE_TO:
786 transform_coord(custom, &el->path.points[i].p1,
787 &g_array_index(barr, BezPoint, i).p1);
789 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
790 renderer_ops->draw_bezier(renderer, (BezPoint *)barr->data,
791 el->path.npoints, &fg);
792 break;
793 case GE_SHAPE:
794 g_array_set_size(barr, el->path.npoints);
795 for (i = 0; i < el->path.npoints; i++)
796 switch (g_array_index(barr,BezPoint,i).type=el->path.points[i].type) {
797 case BEZ_CURVE_TO:
798 transform_coord(custom, &el->path.points[i].p3,
799 &g_array_index(barr, BezPoint, i).p3);
800 transform_coord(custom, &el->path.points[i].p2,
801 &g_array_index(barr, BezPoint, i).p2);
802 case BEZ_MOVE_TO:
803 case BEZ_LINE_TO:
804 transform_coord(custom, &el->path.points[i].p1,
805 &g_array_index(barr, BezPoint, i).p1);
807 if (custom->show_background && el->any.s.fill != DIA_SVG_COLOUR_NONE)
808 renderer_ops->fill_bezier(renderer, (BezPoint *)barr->data,
809 el->path.npoints, &bg);
810 if (el->any.s.stroke != DIA_SVG_COLOUR_NONE)
811 renderer_ops->draw_bezier(renderer, (BezPoint *)barr->data,
812 el->path.npoints, &fg);
813 break;
817 if (custom->info->has_text) {
818 /*Rectangle tb;
820 if (renderer->is_interactive) {
821 transform_rect(custom, &custom->info->text_bounds, &tb);
822 p1.x = tb.left; p1.y = tb.top;
823 p2.x = tb.right; p2.y = tb.bottom;
824 renderer_ops->draw_rect(renderer, &p1, &p2, &custom->border_color);
826 text_draw(custom->text, renderer);
831 static void
832 custom_update_data(Custom *custom, AnchorShape horiz, AnchorShape vert)
834 Element *elem = &custom->element;
835 ShapeInfo *info = custom->info;
836 DiaObject *obj = &elem->object;
837 Point center, bottom_right;
838 Point p;
839 static GArray *arr = NULL, *barr = NULL;
841 int i;
842 GList *tmp;
843 char *txs;
845 /* save starting points */
846 center = bottom_right = elem->corner;
847 center.x += elem->width/2;
848 bottom_right.x += elem->width;
849 center.y += elem->height/2;
850 bottom_right.y += elem->height;
852 /* update the translation coefficients first ... */
853 custom->xscale = elem->width / (info->shape_bounds.right -
854 info->shape_bounds.left);
855 custom->yscale = elem->height / (info->shape_bounds.bottom -
856 info->shape_bounds.top);
858 /* resize shape if text does not fit inside text_bounds */
859 if ((info->has_text) && (info->resize_with_text)) {
860 real text_width, text_height;
861 real xscale = 0.0, yscale = 0.0;
862 Rectangle tb;
864 text_calc_boundingbox(custom->text, NULL);
865 text_width =
866 custom->text->max_width + 2*custom->padding+custom->border_width;
867 text_height = custom->text->height * custom->text->numlines +
868 2 * custom->padding + custom->border_width;
870 transform_rect(custom, &info->text_bounds, &tb);
871 xscale = text_width / (tb.right - tb.left);
873 if (xscale > 1.00000001) {
874 elem->width *= xscale;
875 custom->xscale *= xscale;
877 /* Maybe now we won't need to do Y scaling */
878 transform_rect(custom, &info->text_bounds, &tb);
881 yscale = text_height / (tb.bottom - tb.top);
882 if (yscale > 1.00000001) {
883 elem->height *= yscale;
884 custom->yscale *= yscale;
888 /* if aspect ratio should be fixed, make sure xscale == yscale */
889 switch (info->aspect_type) {
890 case SHAPE_ASPECT_FREE:
891 break;
892 case SHAPE_ASPECT_FIXED:
893 if (custom->xscale != custom->yscale) {
894 /* depending on the moving handle let one scale take precedence */
895 if (ANCHOR_MIDDLE != horiz && ANCHOR_MIDDLE != vert)
896 custom->xscale = custom->yscale = (custom->xscale + custom->yscale) / 2;
897 else if (ANCHOR_MIDDLE == horiz)
898 custom->xscale = custom->yscale;
899 else
900 custom->yscale = custom->xscale;
902 elem->width = custom->xscale * (info->shape_bounds.right -
903 info->shape_bounds.left);
904 elem->height = custom->yscale * (info->shape_bounds.bottom -
905 info->shape_bounds.top);
907 break;
908 case SHAPE_ASPECT_RANGE:
909 if (custom->xscale / custom->yscale < info->aspect_min) {
910 custom->xscale = custom->yscale * info->aspect_min;
911 elem->width = custom->xscale * (info->shape_bounds.right -
912 info->shape_bounds.left);
914 if (custom->xscale / custom->yscale > info->aspect_max) {
915 custom->yscale = custom->xscale / info->aspect_max;
916 elem->height = custom->yscale * (info->shape_bounds.bottom -
917 info->shape_bounds.top);
919 break;
922 /* move shape if necessary ... */
923 switch (horiz) {
924 case ANCHOR_START:
925 break;
926 case ANCHOR_MIDDLE:
927 elem->corner.x = center.x - elem->width/2; break;
928 case ANCHOR_END:
929 elem->corner.x = bottom_right.x - elem->width; break;
931 switch (vert) {
932 case ANCHOR_START:
933 break;
934 case ANCHOR_MIDDLE:
935 elem->corner.y = center.y - elem->height/2; break;
936 case ANCHOR_END:
937 elem->corner.y = bottom_right.y - elem->height; break;
940 /* update transformation coefficients after the possible resize ... */
941 custom->xscale = elem->width / (info->shape_bounds.right -
942 info->shape_bounds.left);
943 custom->yscale = elem->height / (info->shape_bounds.bottom -
944 info->shape_bounds.top);
945 custom->xoffs = elem->corner.x - custom->xscale * info->shape_bounds.left;
946 custom->yoffs = elem->corner.y - custom->yscale * info->shape_bounds.top;
948 /* flip image if required ... */
949 if (custom->flip_h) {
950 custom->xscale = -custom->xscale;
951 custom->xoffs = elem->corner.x -custom->xscale * info->shape_bounds.right;
953 if (custom->flip_v) {
954 custom->yscale = -custom->yscale;
955 custom->yoffs = elem->corner.y -custom->yscale * info->shape_bounds.bottom;
958 /* reposition the text element to the new text bounding box ... */
959 if (info->has_text) {
960 Rectangle tb;
961 transform_rect(custom, &info->text_bounds, &tb);
962 switch (custom->text->alignment) {
963 case ALIGN_LEFT:
964 p.x = tb.left;
965 break;
966 case ALIGN_RIGHT:
967 p.x = tb.right;
968 break;
969 case ALIGN_CENTER:
970 p.x = (tb.left + tb.right) / 2;
971 break;
973 /* align the text to be close to the shape ... */
976 txs = text_get_string_copy(custom->text);
978 if ((tb.bottom+tb.top)/2 > elem->corner.y + elem->height)
979 p.y = tb.top +
980 dia_font_ascent(txs,custom->text->font, custom->text->height);
981 else if ((tb.bottom+tb.top)/2 < elem->corner.y)
982 p.y = tb.bottom + custom->text->height * (custom->text->numlines - 1);
983 else
984 p.y = (tb.top + tb.bottom -
985 custom->text->height * custom->text->numlines) / 2 +
986 dia_font_ascent(txs,custom->text->font, custom->text->height);
987 text_set_position(custom->text, &p);
988 g_free(txs);
991 for (i = 0; i < info->nconnections; i++){
992 transform_coord(custom, &info->connections[i],&custom->connections[i].pos);
994 /* Set the directions for the connection points as hints to the
995 zig-zag line. A hint is only set if the connection point is on
996 the boundary of the shape. */
997 custom->connections[i].directions = 0;
998 if(custom->info->connections[i].x == custom->info->shape_bounds.left)
999 custom->connections[i].directions |= DIR_WEST;
1000 if(custom->info->connections[i].x == custom->info->shape_bounds.right)
1001 custom->connections[i].directions |= DIR_EAST;
1002 if(custom->info->connections[i].y == custom->info->shape_bounds.top)
1003 custom->connections[i].directions |= DIR_NORTH;
1004 if(custom->info->connections[i].y == custom->info->shape_bounds.bottom)
1005 custom->connections[i].directions |= DIR_SOUTH;
1009 elem->extra_spacing.border_trans = 0; /*custom->border_width/2; */
1010 element_update_boundingbox(elem);
1012 /* Merge in the bounding box of every individual element */
1013 if (!arr)
1014 arr = g_array_new(FALSE, FALSE, sizeof(Point));
1015 if (!barr)
1016 barr = g_array_new(FALSE, FALSE, sizeof(BezPoint));
1018 for (tmp = custom->info->display_list; tmp != NULL; tmp = tmp->next) {
1019 GraphicElement *el = tmp->data;
1020 Rectangle rect;
1021 real lwfactor = custom->border_width / 2;
1023 switch(el->type) {
1024 case GE_LINE: {
1025 LineBBExtras extra;
1026 Point p1,p2;
1027 extra.start_trans = extra.end_trans = el->line.s.line_width * lwfactor;
1028 extra.start_long = extra.end_long = 0;
1030 transform_coord(custom, &el->line.p1, &p1);
1031 transform_coord(custom, &el->line.p2, &p2);
1033 line_bbox(&p1,&p2,&extra,&rect);
1034 break;
1036 case GE_POLYLINE: {
1037 PolyBBExtras extra;
1039 extra.start_trans = extra.end_trans = extra.middle_trans =
1040 el->polyline.s.line_width * lwfactor;
1041 extra.start_long = extra.end_long = 0;
1043 g_array_set_size(arr, el->polyline.npoints);
1044 for (i = 0; i < el->polyline.npoints; i++)
1045 transform_coord(custom, &el->polyline.points[i],
1046 &g_array_index(arr, Point, i));
1048 polyline_bbox(&g_array_index(arr,Point,0),el->polyline.npoints,
1049 &extra,FALSE,&rect);
1050 break;
1052 case GE_POLYGON: {
1053 PolyBBExtras extra;
1054 extra.start_trans = extra.end_trans = extra.middle_trans =
1055 el->polygon.s.line_width * lwfactor;
1056 extra.start_long = extra.end_long = 0;
1058 g_array_set_size(arr, el->polygon.npoints);
1059 for (i = 0; i < el->polygon.npoints; i++)
1060 transform_coord(custom, &el->polygon.points[i],
1061 &g_array_index(arr, Point, i));
1063 polyline_bbox(&g_array_index(arr,Point,0),el->polyline.npoints,
1064 &extra,TRUE,&rect);
1065 break;
1067 case GE_PATH: {
1068 PolyBBExtras extra;
1069 extra.start_trans = extra.end_trans = extra.middle_trans =
1070 el->path.s.line_width * lwfactor;
1071 extra.start_long = extra.end_long = 0;
1073 g_array_set_size(barr, el->path.npoints);
1074 for (i = 0; i < el->path.npoints; i++)
1075 switch (g_array_index(barr,BezPoint,i).type=el->path.points[i].type) {
1076 case BEZ_CURVE_TO:
1077 transform_coord(custom, &el->path.points[i].p3,
1078 &g_array_index(barr, BezPoint, i).p3);
1079 transform_coord(custom, &el->path.points[i].p2,
1080 &g_array_index(barr, BezPoint, i).p2);
1081 case BEZ_MOVE_TO:
1082 case BEZ_LINE_TO:
1083 transform_coord(custom, &el->path.points[i].p1,
1084 &g_array_index(barr, BezPoint, i).p1);
1087 polybezier_bbox(&g_array_index(barr,BezPoint,0),el->path.npoints,
1088 &extra,FALSE,&rect);
1089 break;
1091 case GE_SHAPE: {
1092 PolyBBExtras extra;
1093 extra.start_trans = extra.end_trans = extra.middle_trans =
1094 el->shape.s.line_width * lwfactor;
1095 extra.start_long = extra.end_long = 0;
1097 g_array_set_size(barr, el->shape.npoints);
1098 for (i = 0; i < el->shape.npoints; i++)
1099 switch (g_array_index(barr,BezPoint,i).type=el->shape.points[i].type) {
1100 case BEZ_CURVE_TO:
1101 transform_coord(custom, &el->shape.points[i].p3,
1102 &g_array_index(barr, BezPoint, i).p3);
1103 transform_coord(custom, &el->shape.points[i].p2,
1104 &g_array_index(barr, BezPoint, i).p2);
1105 case BEZ_MOVE_TO:
1106 case BEZ_LINE_TO:
1107 transform_coord(custom, &el->shape.points[i].p1,
1108 &g_array_index(barr, BezPoint, i).p1);
1110 polybezier_bbox(&g_array_index(barr,BezPoint,0),el->shape.npoints,
1111 &extra,TRUE,&rect);
1112 break;
1114 case GE_ELLIPSE: {
1115 ElementBBExtras extra;
1116 Point centre;
1117 extra.border_trans = el->ellipse.s.line_width * lwfactor;
1118 transform_coord(custom, &el->ellipse.center, &centre);
1120 ellipse_bbox(&centre,
1121 el->ellipse.width * fabs(custom->xscale),
1122 el->ellipse.height * fabs(custom->yscale),
1123 &extra,&rect);
1124 break;
1126 case GE_RECT: {
1127 ElementBBExtras extra;
1128 Rectangle rin,trin;
1129 rin.left = el->rect.corner1.x;
1130 rin.top = el->rect.corner1.y;
1131 rin.right = el->rect.corner2.x;
1132 rin.bottom = el->rect.corner2.y;
1133 transform_rect(custom, &rin, &trin);
1135 extra.border_trans = el->rect.s.line_width * lwfactor;
1136 rectangle_bbox(&trin,&extra,&rect);
1137 break;
1139 case GE_IMAGE: {
1140 Rectangle bounds;
1142 bounds.left = bounds.right = el->image.topleft.x;
1143 bounds.top = bounds.bottom = el->image.topleft.y;
1144 bounds.right += el->image.width;
1145 bounds.bottom += el->image.height;
1147 transform_rect(custom, &bounds, &rect);
1148 break;
1150 case GE_TEXT:
1151 /*text_calc_boundingbox(el->text.object,&rect); */
1152 rect = el->text.text_bounds;
1153 break;
1155 rectangle_union(&obj->bounding_box,&rect);
1159 /* extend bouding box to include text bounds ... */
1160 if (info->has_text) {
1161 Rectangle tb;
1162 text_calc_boundingbox(custom->text, &tb);
1163 rectangle_union(&obj->bounding_box, &tb);
1166 obj->position = elem->corner;
1168 element_update_handles(elem);
1171 /* reposition the text element to the new text bounding box ... */
1172 void custom_reposition_text(Custom *custom, GraphicElementText *text) {
1173 Element *elem = &custom->element;
1174 Point p;
1175 Rectangle tb;
1177 transform_rect(custom, &text->text_bounds, &tb);
1178 switch (text->object->alignment) {
1179 case ALIGN_LEFT:
1180 p.x = tb.left;
1181 break;
1182 case ALIGN_RIGHT:
1183 p.x = tb.right;
1184 break;
1185 case ALIGN_CENTER:
1186 p.x = (tb.left + tb.right) / 2;
1187 break;
1189 /* align the text to be close to the shape ... */
1191 if ((tb.bottom+tb.top)/2 > elem->corner.y + elem->height)
1192 p.y = tb.top +
1193 dia_font_ascent(text->string,
1194 text->object->font, text->object->height);
1195 else if ((tb.bottom+tb.top)/2 < elem->corner.y)
1196 p.y = tb.bottom + text->object->height * (text->object->numlines - 1);
1197 else
1198 p.y = (tb.top + tb.bottom -
1199 text->object->height * text->object->numlines) / 2 +
1200 dia_font_ascent(text->string,
1201 text->object->font, text->object->height);
1202 text_set_position(text->object, &p);
1203 return;
1206 static DiaObject *
1207 custom_create(Point *startpoint,
1208 void *user_data,
1209 Handle **handle1,
1210 Handle **handle2)
1212 Custom *custom;
1213 Element *elem;
1214 DiaObject *obj;
1215 ShapeInfo *info = (ShapeInfo *)user_data;
1216 Point p;
1217 int i;
1218 DiaFont *font = NULL;
1219 real font_height;
1221 g_return_val_if_fail(info!=NULL,NULL);
1223 init_default_values();
1225 custom = g_new0_ext (Custom, info->ext_attr_size);
1226 elem = &custom->element;
1227 obj = &elem->object;
1229 obj->type = info->object_type;
1231 obj->ops = &custom_ops;
1233 elem->corner = *startpoint;
1234 elem->width = DEFAULT_WIDTH;
1235 elem->height = DEFAULT_HEIGHT;
1237 custom->info = info;
1239 custom->border_width = attributes_get_default_linewidth();
1240 custom->border_color = attributes_get_foreground();
1241 custom->inner_color = attributes_get_background();
1242 custom->show_background = default_properties.show_background;
1243 attributes_get_default_line_style(&custom->line_style, &custom->dashlength);
1245 custom->padding = default_properties.padding;
1247 custom->flip_h = FALSE;
1248 custom->flip_v = FALSE;
1250 if (info->has_text) {
1251 attributes_get_default_font(&font, &font_height);
1252 p = *startpoint;
1253 p.x += elem->width / 2.0;
1254 p.y += elem->height / 2.0 + font_height / 2;
1255 custom->text = new_text("", font, font_height, &p, &custom->border_color,
1256 default_properties.alignment);
1257 text_get_attributes(custom->text,&custom->attrs);
1258 dia_font_unref(font);
1260 shape_info_realise(custom->info);
1261 element_init(elem, 8, info->nconnections);
1263 custom->connections = g_new0(ConnectionPoint, info->nconnections);
1264 for (i = 0; i < info->nconnections; i++) {
1265 obj->connections[i] = &custom->connections[i];
1266 custom->connections[i].object = obj;
1267 custom->connections[i].connected = NULL;
1268 custom->connections[i].flags = 0;
1269 if (i == info->main_cp) {
1270 custom->connections[i].flags = CP_FLAGS_MAIN;
1274 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
1276 *handle1 = NULL;
1277 *handle2 = obj->handles[7];
1278 return &custom->element.object;
1281 static void
1282 custom_destroy(Custom *custom)
1284 if (custom->info->has_text)
1285 text_destroy(custom->text);
1288 * custom_destroy is called per object. It _must not_ destroy class stuff
1289 * (ShapeInfo) cause it does not hold a reference to it. Fixes e.g.
1290 * bug #158288, #160550, ...
1291 * DONT TOUCH : custom->info->display_list
1294 /* TODO: free allocated ext props (string, etc.) */
1296 element_destroy(&custom->element);
1298 g_free(custom->connections);
1301 static DiaObject *
1302 custom_copy(Custom *custom)
1304 int i;
1305 Custom *newcustom;
1306 Element *elem, *newelem;
1307 DiaObject *newobj;
1309 elem = &custom->element;
1311 newcustom = g_new0_ext (Custom, custom->info->ext_attr_size);
1312 newelem = &newcustom->element;
1313 newobj = &newcustom->element.object;
1315 element_copy(elem, newelem);
1317 newcustom->info = custom->info;
1319 newcustom->border_width = custom->border_width;
1320 newcustom->border_color = custom->border_color;
1321 newcustom->inner_color = custom->inner_color;
1322 newcustom->show_background = custom->show_background;
1323 newcustom->line_style = custom->line_style;
1324 newcustom->dashlength = custom->dashlength;
1325 newcustom->padding = custom->padding;
1327 newcustom->flip_h = custom->flip_h;
1328 newcustom->flip_v = custom->flip_v;
1330 if (custom->info->has_text) {
1331 newcustom->text = text_copy(custom->text);
1332 text_get_attributes(newcustom->text,&newcustom->attrs);
1335 if (custom->info->ext_attr_size) /* copy ext area past end */
1336 memcpy (newcustom + 1, custom + 1, custom->info->ext_attr_size);
1338 newcustom->connections = g_new0(ConnectionPoint, custom->info->nconnections);
1340 for (i = 0; i < custom->info->nconnections; i++) {
1341 newobj->connections[i] = &newcustom->connections[i];
1342 newcustom->connections[i].object = newobj;
1343 newcustom->connections[i].connected = NULL;
1344 newcustom->connections[i].pos = custom->connections[i].pos;
1345 newcustom->connections[i].directions = custom->connections[i].directions;
1346 newcustom->connections[i].last_pos = custom->connections[i].last_pos;
1347 newcustom->connections[i].flags = custom->connections[i].flags;
1350 return &newcustom->element.object;
1353 static DiaObject *
1354 custom_load_using_properties(ObjectNode obj_node, int version, const char *filename)
1356 Custom *custom;
1357 DiaObject *obj;
1358 Point startpoint = {0.0,0.0};
1359 Handle *handle1,*handle2;
1361 obj = custom_type.ops->create(&startpoint, shape_info_get(obj_node), &handle1, &handle2);
1362 custom = (Custom*)obj;
1363 object_load_props(obj,obj_node);
1365 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
1367 return obj;
1370 struct CustomObjectChange {
1371 ObjectChange objchange;
1373 enum { CHANGE_FLIPH, CHANGE_FLIPV} type;
1374 gboolean old_val;
1377 static void
1378 custom_change_apply(struct CustomObjectChange *change, Custom *custom)
1380 switch (change->type) {
1381 case CHANGE_FLIPH:
1382 custom->flip_h = !change->old_val;
1383 break;
1384 case CHANGE_FLIPV:
1385 custom->flip_v = !change->old_val;
1386 break;
1389 static void
1390 custom_change_revert(struct CustomObjectChange *change, Custom *custom)
1392 switch (change->type) {
1393 case CHANGE_FLIPH:
1394 custom->flip_h = change->old_val;
1395 break;
1396 case CHANGE_FLIPV:
1397 custom->flip_v = change->old_val;
1398 break;
1402 static ObjectChange *
1403 custom_flip_h_callback (DiaObject *obj, Point *clicked, gpointer data)
1405 Custom *custom = (Custom *)obj;
1406 struct CustomObjectChange *change = g_new0(struct CustomObjectChange, 1);
1408 change->objchange.apply = (ObjectChangeApplyFunc)custom_change_apply;
1409 change->objchange.revert = (ObjectChangeRevertFunc)custom_change_revert;
1410 change->objchange.free = NULL;
1411 change->type = CHANGE_FLIPH;
1412 change->old_val = custom->flip_h;
1414 custom->flip_h = !custom->flip_h;
1415 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
1417 return &change->objchange;
1420 static ObjectChange *
1421 custom_flip_v_callback (DiaObject *obj, Point *clicked, gpointer data)
1423 Custom *custom = (Custom *)obj;
1424 struct CustomObjectChange *change = g_new0(struct CustomObjectChange, 1);
1426 change->objchange.apply = (ObjectChangeApplyFunc)custom_change_apply;
1427 change->objchange.revert = (ObjectChangeRevertFunc)custom_change_revert;
1428 change->objchange.free = NULL;
1429 change->type = CHANGE_FLIPV;
1430 change->old_val = custom->flip_v;
1432 custom->flip_v = !custom->flip_v;
1433 custom_update_data(custom, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
1435 return &change->objchange;
1438 static DiaMenuItem custom_menu_items[] = {
1439 { N_("Flip Horizontal"), custom_flip_h_callback, NULL, 1 },
1440 { N_("Flip Vertical"), custom_flip_v_callback, NULL, 1 },
1443 static DiaMenu custom_menu = {
1444 "Custom",
1445 sizeof(custom_menu_items)/sizeof(DiaMenuItem),
1446 custom_menu_items,
1447 NULL
1450 static DiaMenu *
1451 custom_get_object_menu(Custom *custom, Point *clickedpoint)
1453 if (custom_menu.title && custom->info->name &&
1454 (0 != strcmp(custom_menu.title,custom->info->name))) {
1455 if (custom_menu.app_data_free) custom_menu.app_data_free(&custom_menu);
1457 custom_menu.title = custom->info->name;
1458 return &custom_menu;
1461 void
1462 custom_object_new(ShapeInfo *info, DiaObjectType **otype)
1464 DiaObjectType *obj = g_new0(DiaObjectType, 1);
1466 *obj = custom_type;
1468 obj->name = info->name;
1469 obj->default_user_data = info;
1471 if (info->icon) {
1472 struct stat buf;
1473 if (0==stat(info->icon,&buf)) {
1474 obj->pixmap = NULL;
1475 obj->pixmap_file = info->icon;
1476 } else {
1477 g_warning(_("Cannot open icon file %s for object type '%s'."),
1478 info->icon,obj->name);
1482 info->object_type = obj;
1484 *otype = obj;