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.
37 #include "shape_info.h"
40 #include "connectionpoint.h"
41 #include "diarenderer.h"
42 #include "attributes.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 */
66 typedef struct _Custom Custom
;
72 /* transformation coords */
76 ConnectionPoint
*connections
;
80 gboolean show_background
;
84 gboolean flip_h
, flip_v
;
91 typedef struct _CustomProperties
{
94 gboolean show_background
;
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
,
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 */
145 DiaObjectType custom_type
=
147 "Custom - Generic", /* name */
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
},
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
,
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
},
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
) },
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
)},
241 void custom_setup_properties (ShapeInfo
*info
, xmlNodePtr node
)
245 int n_props
, offs
= 0;
248 /* count ext_attributes node items */
251 for (i
= 0, cur
= node
->xmlChildrenNode
; cur
!= NULL
; cur
= cur
->next
)
253 if ((!xmlIsBlankNode(cur
)) && (cur
->type
== XML_ELEMENT_NODE
))
256 info
->n_ext_attr
= i
;
259 /* create prop tables & initialize constant part */
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
));
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
));
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
))
285 if (node
->type
!= XML_ELEMENT_NODE
)
287 if (!strcmp(node
->name
, "ext_attribute"))
289 gchar
*pname
, *ptype
= 0;
291 str
= xmlGetProp(node
, "name");
294 pname
= g_strdup(str
);
297 str
= xmlGetProp(node
, "type");
303 ptype
= g_strdup(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");
315 pname
= g_strdup(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 */
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
;
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
;
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
;
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
);
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
);
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;
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
;
397 transform_rect(Custom
*custom
, const Rectangle
*r1
, Rectangle
*out
)
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
) {
407 out
->left
= out
->right
;
410 if (out
->top
> out
->bottom
) {
412 out
->top
= out
->bottom
;
418 custom_distance_from(Custom
*custom
, Point
*point
)
420 static GArray
*arr
= NULL
, *barr
= NULL
;
425 real min_dist
= G_MAXFLOAT
, dist
= G_MAXFLOAT
;
428 arr
= g_array_new(FALSE
, FALSE
, sizeof(Point
));
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
;
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
);
443 transform_coord(custom
, &el
->polyline
.points
[0], &p1
);
445 for (i
= 1; i
< el
->polyline
.npoints
; i
++) {
448 transform_coord(custom
, &el
->polyline
.points
[i
], &p2
);
449 seg_dist
= distance_line_point(&p1
, &p2
, line_width
, point
);
451 dist
= MIN(dist
, seg_dist
);
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
,
465 transform_coord(custom
, &el
->rect
.corner1
, &p1
);
466 transform_coord(custom
, &el
->rect
.corner2
, &p2
);
468 rect
.left
= p1
.x
- line_width
/2; rect
.right
= p2
.x
+ line_width
/2;
470 rect
.left
= p2
.x
- line_width
/2; rect
.right
= p1
.x
+ line_width
/2;
473 rect
.top
= p1
.y
- line_width
/2; rect
.bottom
= p2
.y
+ line_width
/2;
475 rect
.top
= p2
.y
- line_width
/2; rect
.bottom
= p1
.y
+ line_width
/2;
477 dist
= distance_rectangle_point(&rect
, point
);
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
);
489 dist
= distance_rectangle_point(&rect
, point
);
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
);
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
),
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
) {
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
);
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
,
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
) {
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
);
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
,
538 min_dist
= MIN(min_dist
, dist
);
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
);
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
);
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
);
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;
594 custom_update_data(custom
, horiz
, vert
);
600 custom_move(Custom
*custom
, Point
*to
)
602 custom
->element
.corner
= *to
;
604 custom_update_data(custom
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
610 get_colour(Custom
*custom
, Color
*colour
, gint32 c
)
613 case DIA_SVG_COLOUR_NONE
:
615 case DIA_SVG_COLOUR_FOREGROUND
:
616 *colour
= custom
->border_color
;
618 case DIA_SVG_COLOUR_BACKGROUND
:
619 *colour
= custom
->inner_color
;
621 case DIA_SVG_COLOUR_TEXT
:
622 *colour
= custom
->text
->color
;
625 colour
->red
= ((c
& 0xff0000) >> 16) / 255.0;
626 colour
->green
= ((c
& 0x00ff00) >> 8) / 255.0;
627 colour
->blue
= (c
& 0x0000ff) / 255.0;
633 custom_draw(Custom
*custom
, DiaRenderer
*renderer
)
635 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
636 static GArray
*arr
= NULL
, *barr
= NULL
;
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
);
651 arr
= g_array_new(FALSE
, FALSE
, sizeof(Point
));
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
;
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
);
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
);
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
,
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
,
727 if (el
->any
.s
.stroke
!= DIA_SVG_COLOUR_NONE
)
728 renderer_ops
->draw_polygon(renderer
,
729 (Point
*)arr
->data
, el
->polygon
.npoints
,
733 transform_coord(custom
, &el
->rect
.corner1
, &p1
);
734 transform_coord(custom
, &el
->rect
.corner2
, &p2
);
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
);
751 custom_reposition_text(custom
, &el
->text
);
752 text_draw(el
->text
.object
, renderer
);
753 text_set_position(el
->text
.object
, &el
->text
.anchor
);
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
),
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
),
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
),
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
) {
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
);
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
);
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
) {
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
);
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
);
817 if (custom
->info
->has_text
) {
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
);
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
;
839 static GArray
*arr
= NULL
, *barr
= NULL
;
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;
864 text_calc_boundingbox(custom
->text
, NULL
);
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
:
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
;
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
);
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
);
922 /* move shape if necessary ... */
927 elem
->corner
.x
= center
.x
- elem
->width
/2; break;
929 elem
->corner
.x
= bottom_right
.x
- elem
->width
; break;
935 elem
->corner
.y
= center
.y
- elem
->height
/2; break;
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
) {
961 transform_rect(custom
, &info
->text_bounds
, &tb
);
962 switch (custom
->text
->alignment
) {
970 p
.x
= (tb
.left
+ tb
.right
) / 2;
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
)
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);
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
);
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 */
1014 arr
= g_array_new(FALSE
, FALSE
, sizeof(Point
));
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
;
1021 real lwfactor
= custom
->border_width
/ 2;
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
);
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
);
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
,
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
) {
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
);
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
);
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
) {
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
);
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
,
1115 ElementBBExtras extra
;
1117 extra
.border_trans
= el
->ellipse
.s
.line_width
* lwfactor
;
1118 transform_coord(custom
, &el
->ellipse
.center
, ¢re
);
1120 ellipse_bbox(¢re
,
1121 el
->ellipse
.width
* fabs(custom
->xscale
),
1122 el
->ellipse
.height
* fabs(custom
->yscale
),
1127 ElementBBExtras extra
;
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
);
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
);
1151 /*text_calc_boundingbox(el->text.object,&rect); */
1152 rect
= el
->text
.text_bounds
;
1155 rectangle_union(&obj
->bounding_box
,&rect
);
1159 /* extend bouding box to include text bounds ... */
1160 if (info
->has_text
) {
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
;
1177 transform_rect(custom
, &text
->text_bounds
, &tb
);
1178 switch (text
->object
->alignment
) {
1186 p
.x
= (tb
.left
+ tb
.right
) / 2;
1189 /* align the text to be close to the shape ... */
1191 if ((tb
.bottom
+tb
.top
)/2 > elem
->corner
.y
+ elem
->height
)
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);
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
);
1207 custom_create(Point
*startpoint
,
1215 ShapeInfo
*info
= (ShapeInfo
*)user_data
;
1218 DiaFont
*font
= NULL
;
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
);
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
);
1277 *handle2
= obj
->handles
[7];
1278 return &custom
->element
.object
;
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
);
1302 custom_copy(Custom
*custom
)
1306 Element
*elem
, *newelem
;
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
;
1354 custom_load_using_properties(ObjectNode obj_node
, int version
, const char *filename
)
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
);
1370 struct CustomObjectChange
{
1371 ObjectChange objchange
;
1373 enum { CHANGE_FLIPH
, CHANGE_FLIPV
} type
;
1378 custom_change_apply(struct CustomObjectChange
*change
, Custom
*custom
)
1380 switch (change
->type
) {
1382 custom
->flip_h
= !change
->old_val
;
1385 custom
->flip_v
= !change
->old_val
;
1390 custom_change_revert(struct CustomObjectChange
*change
, Custom
*custom
)
1392 switch (change
->type
) {
1394 custom
->flip_h
= change
->old_val
;
1397 custom
->flip_v
= change
->old_val
;
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
= {
1445 sizeof(custom_menu_items
)/sizeof(DiaMenuItem
),
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
;
1462 custom_object_new(ShapeInfo
*info
, DiaObjectType
**otype
)
1464 DiaObjectType
*obj
= g_new0(DiaObjectType
, 1);
1468 obj
->name
= info
->name
;
1469 obj
->default_user_data
= info
;
1473 if (0==stat(info
->icon
,&buf
)) {
1475 obj
->pixmap_file
= info
->icon
;
1477 g_warning(_("Cannot open icon file %s for object type '%s'."),
1478 info
->icon
,obj
->name
);
1482 info
->object_type
= obj
;