1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 * Copyright (C) 2003 W. Borgert <debacle@debian.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * Derived from association.c and implements.c
22 * 2003-08-13, 2003-08-15: W. Borgert <debacle@debian.org>
35 #include "orth_conn.h"
36 #include "connectionpoint.h"
37 #include "diarenderer.h"
39 #include "properties.h"
43 #include "pixmaps/facet.xpm"
45 typedef struct _Compfeat Compfeat
;
54 static int compprop_arrow
[] = {
75 #define COMPPROP_WIDTH 0.1
76 #define COMPPROP_FONTHEIGHT 0.8
77 #define COMPPROP_DIAMETER 0.8
78 #define COMPPROP_DEFAULTLEN 2.0
79 #define COMPPROP_TEXTOFFSET 1.0
80 #define HANDLE_MOVE_TEXT (HANDLE_CUSTOM2)
82 static ObjectChange
* compfeat_move_handle(Compfeat
*compfeat
,
86 HandleMoveReason reason
,
87 ModifierKeys modifiers
);
88 static ObjectChange
* compfeat_move(Compfeat
*compfeat
, Point
*to
);
89 static void compfeat_select(Compfeat
*compfeat
, Point
*clicked_point
,
90 DiaRenderer
*interactive_renderer
);
91 static void compfeat_draw(Compfeat
*compfeat
, DiaRenderer
*renderer
);
92 static DiaObject
*compfeat_create(Point
*startpoint
,
96 static real
compfeat_distance_from(Compfeat
*compfeat
, Point
*point
);
97 static void compfeat_update_data(Compfeat
*compfeat
);
98 static void compfeat_destroy(Compfeat
*compfeat
);
99 static DiaMenu
*compfeat_get_object_menu(Compfeat
*compfeat
,
100 Point
*clickedpoint
);
101 static PropDescription
*compfeat_describe_props(Compfeat
*compfeat
);
102 static void compfeat_get_props(Compfeat
* compfeat
, GPtrArray
*props
);
103 static void compfeat_set_props(Compfeat
* compfeat
, GPtrArray
*props
);
104 static DiaObject
*compfeat_load(ObjectNode obj_node
, int version
,
105 const char *filename
);
108 static ObjectTypeOps compfeat_type_ops
=
110 (CreateFunc
) compfeat_create
,
111 (LoadFunc
) compfeat_load
,
112 (SaveFunc
) object_save_using_properties
,
113 (GetDefaultsFunc
) NULL
,
114 (ApplyDefaultsFunc
) NULL
117 DiaObjectType compfeat_type
=
119 "UML - Component Feature", /* name */
120 /* Version 0 had no autorouting and so shouldn't have it set by default. */
122 (char **) facet_xpm
, /* pixmap */
123 &compfeat_type_ops
, /* ops */
124 NULL
, /* pixmap file */
125 0 /* default user data */
128 static ObjectOps compfeat_ops
= {
129 (DestroyFunc
) compfeat_destroy
,
130 (DrawFunc
) compfeat_draw
,
131 (DistanceFunc
) compfeat_distance_from
,
132 (SelectFunc
) compfeat_select
,
133 (CopyFunc
) object_copy_using_properties
,
134 (MoveFunc
) compfeat_move
,
135 (MoveHandleFunc
) compfeat_move_handle
,
136 (GetPropertiesFunc
) object_create_props_dialog
,
137 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
138 (ObjectMenuFunc
) compfeat_get_object_menu
,
139 (DescribePropsFunc
) compfeat_describe_props
,
140 (GetPropsFunc
) compfeat_get_props
,
141 (SetPropsFunc
) compfeat_set_props
144 static PropEnumData prop_compfeat_type_data
[] = {
145 { N_("Facet"), COMPPROP_FACET
},
146 { N_("Receptacle"), COMPPROP_RECEPTACLE
},
147 { N_("Event Source"), COMPPROP_EVENTSOURCE
},
148 { N_("Event Sink"), COMPPROP_EVENTSINK
},
152 static PropDescription compfeat_props
[] = {
153 ORTHCONN_COMMON_PROPERTIES
,
154 { "role", PROP_TYPE_ENUM
, 0, NULL
, NULL
, prop_compfeat_type_data
},
155 { "text", PROP_TYPE_TEXT
, 0, N_("Text"), NULL
, NULL
},
157 PROP_STD_TEXT_HEIGHT
,
158 PROP_STD_TEXT_COLOUR
,
159 PROP_STD_TEXT_ALIGNMENT
,
160 { "text_pos", PROP_TYPE_POINT
, 0, NULL
, NULL
, NULL
},
164 static ObjectChange
*
165 compfeat_add_segment_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
167 ObjectChange
*change
;
168 change
= orthconn_add_segment((OrthConn
*)obj
, clicked
);
169 compfeat_update_data((Compfeat
*)obj
);
173 static ObjectChange
*
174 compfeat_delete_segment_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
176 ObjectChange
*change
;
178 change
= orthconn_delete_segment((OrthConn
*)obj
, clicked
);
179 compfeat_update_data((Compfeat
*)obj
);
183 static DiaMenuItem object_menu_items
[] = {
184 { N_("Add segment"), compfeat_add_segment_callback
, NULL
, 1 },
185 { N_("Delete segment"), compfeat_delete_segment_callback
, NULL
, 1 },
186 ORTHCONN_COMMON_MENUS
,
189 static DiaMenu object_menu
= {
191 sizeof(object_menu_items
)/sizeof(DiaMenuItem
),
197 compfeat_get_object_menu(Compfeat
*compfeat
, Point
*clickedpoint
)
201 orth
= &compfeat
->orth
;
202 /* Set entries sensitive/selected etc here */
203 object_menu_items
[0].active
=
204 orthconn_can_add_segment(orth
, clickedpoint
);
205 object_menu_items
[1].active
=
206 orthconn_can_delete_segment(orth
, clickedpoint
);
207 orthconn_update_object_menu(orth
, clickedpoint
, &object_menu_items
[2]);
211 static PropDescription
*
212 compfeat_describe_props(Compfeat
*compfeat
)
214 if (compfeat_props
[0].quark
== 0)
215 prop_desc_list_calculate_quarks(compfeat_props
);
216 return compfeat_props
;
219 static PropOffset compfeat_offsets
[] = {
220 ORTHCONN_COMMON_PROPERTIES_OFFSETS
,
221 { "role", PROP_TYPE_ENUM
, offsetof(Compfeat
, role
) },
222 { "text", PROP_TYPE_TEXT
, offsetof(Compfeat
, text
) },
223 { "text_font", PROP_TYPE_FONT
, offsetof(Compfeat
, attrs
.font
) },
224 { "text_height", PROP_TYPE_REAL
, offsetof(Compfeat
, attrs
.height
) },
225 { "text_colour", PROP_TYPE_COLOUR
, offsetof(Compfeat
, attrs
.color
) },
226 { "text_alignment", PROP_TYPE_ENUM
, offsetof(Compfeat
, attrs
.alignment
) },
227 { "text_pos", PROP_TYPE_POINT
, offsetof(Compfeat
, text_pos
) },
232 compfeat_get_props(Compfeat
*compfeat
, GPtrArray
*props
)
234 if (compfeat
->roletmp
)
235 compfeat
->role
= compfeat
->roletmp
;
236 text_set_position(compfeat
->text
, &compfeat
->text_pos
);
237 text_get_attributes(compfeat
->text
, &compfeat
->attrs
);
238 object_get_props_from_offsets(&compfeat
->orth
.object
,
239 compfeat_offsets
, props
);
243 compfeat_set_props(Compfeat
*compfeat
, GPtrArray
*props
)
245 object_set_props_from_offsets(&compfeat
->orth
.object
,
246 compfeat_offsets
, props
);
247 compfeat
->text_handle
.pos
= compfeat
->text_pos
;
248 text_set_position(compfeat
->text
, &compfeat
->text_handle
.pos
);
249 apply_textattr_properties(props
, compfeat
->text
, "text", &compfeat
->attrs
);
250 compfeat_update_data(compfeat
);
254 compfeat_distance_from(Compfeat
*compfeat
, Point
*point
)
256 OrthConn
*orth
= &compfeat
->orth
;
257 return orthconn_distance_from(orth
, point
, COMPPROP_WIDTH
);
261 compfeat_select(Compfeat
*compfeat
, Point
*clicked_point
,
262 DiaRenderer
*interactive_renderer
)
264 text_set_cursor(compfeat
->text
, clicked_point
, interactive_renderer
);
265 text_grab_focus(compfeat
->text
, &compfeat
->orth
.object
);
266 orthconn_update_data(&compfeat
->orth
);
269 static ObjectChange
*
270 compfeat_move_handle(Compfeat
*compfeat
, Handle
*handle
,
271 Point
*to
, ConnectionPoint
*cp
,
272 HandleMoveReason reason
,
273 ModifierKeys modifiers
)
275 ObjectChange
*change
;
277 assert(compfeat
!=NULL
);
278 assert(handle
!=NULL
);
281 if (handle
->id
== HANDLE_MOVE_TEXT
) {
282 text_set_position(compfeat
->text
, to
);
285 change
= orthconn_move_handle(&compfeat
->orth
, handle
, to
, cp
,
288 compfeat_update_data(compfeat
);
293 static ObjectChange
*
294 compfeat_move(Compfeat
*compfeat
, Point
*to
)
296 ObjectChange
*change
;
300 point_sub(&delta
, &compfeat
->orth
.points
[0]);
302 /* I don't understand this, but the text position is wrong directly
303 after compfeat_create()! */
304 point_add(&delta
, &compfeat
->text
->position
);
305 text_set_position(compfeat
->text
, &delta
);
306 change
= orthconn_move(&compfeat
->orth
, to
);
307 compfeat_update_data(compfeat
);
313 compfeat_draw(Compfeat
*compfeat
, DiaRenderer
*renderer
)
315 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
317 OrthConn
*orth
= &compfeat
->orth
;
320 Arrow startarrow
, endarrow
;
322 assert(compfeat
!= NULL
);
323 assert(renderer
!= NULL
);
325 points
= &orth
->points
[0];
328 renderer_ops
->set_linewidth(renderer
, COMPPROP_WIDTH
);
329 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
330 renderer_ops
->set_linecaps(renderer
, LINECAPS_BUTT
);
332 if (compfeat
->orth
.orientation
[orth
->numorient
- 1] == HORIZONTAL
) {
333 directions
= (points
[n
- 1].x
> points
[n
- 2].x
)? DIR_EAST
: DIR_WEST
;
335 directions
= (points
[n
- 1].y
> points
[n
- 2].y
)? DIR_SOUTH
: DIR_NORTH
;
338 if (compfeat
->role
== COMPPROP_FACET
339 || compfeat
->role
== COMPPROP_EVENTSOURCE
)
340 compfeat
->cp
.directions
= directions
;
342 startarrow
.type
= ARROW_NONE
;
343 startarrow
.length
= COMPPROP_DIAMETER
;
344 startarrow
.width
= COMPPROP_DIAMETER
;
345 endarrow
.length
= COMPPROP_DIAMETER
;
346 endarrow
.width
= COMPPROP_DIAMETER
;
347 endarrow
.type
= compprop_arrow
[compfeat
->role
];
348 renderer_ops
->draw_polyline_with_arrows(renderer
, points
, n
,
351 &startarrow
, &endarrow
);
353 text_draw(compfeat
->text
, renderer
);
357 compfeat_create(Point
*startpoint
,
368 font
= dia_font_new_from_style(DIA_FONT_MONOSPACE
, 0.8);
370 compfeat
= g_new0(Compfeat
, 1);
371 compfeat
->role
= compfeat
->roletmp
= GPOINTER_TO_INT(user_data
);
373 orth
= &compfeat
->orth
;
376 obj
->type
= &compfeat_type
;
377 obj
->ops
= &compfeat_ops
;
379 orthconn_init(orth
, startpoint
);
382 p
.y
-= COMPPROP_TEXTOFFSET
;
384 compfeat
->text
= new_text("", font
,
385 COMPPROP_FONTHEIGHT
, &p
, &color_black
,
387 dia_font_unref(font
);
388 text_get_attributes(compfeat
->text
, &compfeat
->attrs
);
390 compfeat
->text_handle
.id
= HANDLE_MOVE_TEXT
;
391 compfeat
->text_handle
.type
= HANDLE_MINOR_CONTROL
;
392 compfeat
->text_handle
.connect_type
= HANDLE_NONCONNECTABLE
;
393 compfeat
->text_handle
.connected_to
= NULL
;
394 compfeat
->text_handle
.pos
= compfeat
->text_pos
= p
;
395 object_add_handle(obj
, &compfeat
->text_handle
);
397 if (compfeat
->role
== COMPPROP_FACET
398 || compfeat
->role
== COMPPROP_EVENTSOURCE
) {
399 object_add_connectionpoint(&orth
->object
, &compfeat
->cp
);
400 obj
->connections
[0] = &compfeat
->cp
;
401 compfeat
->cp
.object
= obj
;
402 compfeat
->cp
.connected
= NULL
;
405 compfeat_update_data(compfeat
);
407 *handle1
= orth
->handles
[0];
408 *handle2
= orth
->handles
[orth
->numpoints
-2];
410 return &compfeat
->orth
.object
;
415 compfeat_destroy(Compfeat
*compfeat
)
417 text_destroy(compfeat
->text
);
418 orthconn_destroy(&compfeat
->orth
);
422 compfeat_update_data(Compfeat
*compfeat
)
424 OrthConn
*orth
= &compfeat
->orth
;
425 PolyBBExtras
*extra
= &orth
->extra_spacing
;
427 DiaObject
*obj
= &orth
->object
;
431 points
= &orth
->points
[0];
434 obj
->position
= points
[0];
436 if (compfeat
->role
== COMPPROP_FACET
437 || compfeat
->role
== COMPPROP_EVENTSOURCE
)
438 compfeat
->cp
.pos
= points
[n
- 1];
440 compfeat
->text_pos
= compfeat
->text_handle
.pos
= compfeat
->text
->position
;
442 orthconn_update_data(orth
);
447 extra
->end_long
= COMPPROP_WIDTH
+ COMPPROP_DIAMETER
;
448 extra
->end_trans
= COMPPROP_WIDTH
+ COMPPROP_DIAMETER
;
450 orthconn_update_boundingbox(orth
);
451 text_calc_boundingbox(compfeat
->text
, &rect
);
452 rectangle_union(&obj
->bounding_box
, &rect
);
456 compfeat_load(ObjectNode obj_node
, int version
, const char *filename
)
458 DiaObject
*obj
= object_load_using_properties(&compfeat_type
,
459 obj_node
,version
,filename
);
462 /* In old objects with no autorouting, set it to false. */
463 attr
= object_find_attribute(obj_node
, "autorouting");
465 ((OrthConn
*)obj
)->autorouting
= FALSE
;