2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / UML / component_feature.c
blob048e6721a7b07aea784bcc8f50a9da9531c9823d
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>
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <assert.h>
30 #include <math.h>
31 #include <string.h>
33 #include "intl.h"
34 #include "object.h"
35 #include "orth_conn.h"
36 #include "connectionpoint.h"
37 #include "diarenderer.h"
38 #include "handle.h"
39 #include "properties.h"
40 #include "text.h"
41 #include "arrows.h"
43 #include "pixmaps/facet.xpm"
45 typedef struct _Compfeat Compfeat;
47 typedef enum {
48 COMPPROP_FACET,
49 COMPPROP_RECEPTACLE,
50 COMPPROP_EVENTSOURCE,
51 COMPPROP_EVENTSINK,
52 } CompRole;
54 static int compprop_arrow[] = {
55 ARROW_HOLLOW_ELLIPSE,
56 ARROW_OPEN_ROUNDED,
57 ARROW_HOLLOW_DIAMOND,
58 ARROW_HALF_DIAMOND,
61 struct _Compfeat {
62 OrthConn orth;
64 ConnectionPoint cp;
66 CompRole role;
67 CompRole roletmp;
69 Text *text;
70 TextAttributes attrs;
71 Point text_pos;
72 Handle text_handle;
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,
83 Handle *handle,
84 Point *to,
85 ConnectionPoint *cp,
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,
93 void *user_data,
94 Handle **handle1,
95 Handle **handle2);
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. */
121 1, /* version */
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 },
149 { NULL, 0}
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 },
156 PROP_STD_TEXT_FONT,
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 },
161 PROP_DESC_END
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);
170 return change;
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);
180 return change;
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 = {
190 "Association",
191 sizeof(object_menu_items)/sizeof(DiaMenuItem),
192 object_menu_items,
193 NULL
196 static DiaMenu *
197 compfeat_get_object_menu(Compfeat *compfeat, Point *clickedpoint)
199 OrthConn *orth;
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]);
208 return &object_menu;
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) },
228 { NULL, 0, 0 }
231 static void
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);
242 static void
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);
253 static real
254 compfeat_distance_from(Compfeat *compfeat, Point *point)
256 OrthConn *orth = &compfeat->orth;
257 return orthconn_distance_from(orth, point, COMPPROP_WIDTH);
260 static void
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);
279 assert(to!=NULL);
281 if (handle->id == HANDLE_MOVE_TEXT) {
282 text_set_position(compfeat->text, to);
283 change = NULL;
284 } else {
285 change = orthconn_move_handle(&compfeat->orth, handle, to, cp,
286 reason, modifiers);
288 compfeat_update_data(compfeat);
290 return change;
293 static ObjectChange *
294 compfeat_move(Compfeat *compfeat, Point *to)
296 ObjectChange *change;
297 Point delta = *to;
299 delta = *to;
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);
309 return change;
312 static void
313 compfeat_draw(Compfeat *compfeat, DiaRenderer *renderer)
315 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
316 Point *points;
317 OrthConn *orth = &compfeat->orth;
318 int n;
319 gchar directions;
320 Arrow startarrow, endarrow;
322 assert(compfeat != NULL);
323 assert(renderer != NULL);
325 points = &orth->points[0];
326 n = orth->numpoints;
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;
334 } else {
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,
349 COMPPROP_WIDTH,
350 &color_black,
351 &startarrow, &endarrow);
353 text_draw(compfeat->text, renderer);
356 static DiaObject *
357 compfeat_create(Point *startpoint,
358 void *user_data,
359 Handle **handle1,
360 Handle **handle2)
362 Compfeat *compfeat;
363 OrthConn *orth;
364 DiaObject *obj;
365 Point p;
366 DiaFont *font;
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;
374 obj = &orth->object;
376 obj->type = &compfeat_type;
377 obj->ops = &compfeat_ops;
379 orthconn_init(orth, startpoint);
381 p = *startpoint;
382 p.y -= COMPPROP_TEXTOFFSET;
384 compfeat->text = new_text("", font,
385 COMPPROP_FONTHEIGHT, &p, &color_black,
386 ALIGN_CENTER);
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;
414 static void
415 compfeat_destroy(Compfeat *compfeat)
417 text_destroy(compfeat->text);
418 orthconn_destroy(&compfeat->orth);
421 static void
422 compfeat_update_data(Compfeat *compfeat)
424 OrthConn *orth = &compfeat->orth;
425 PolyBBExtras *extra = &orth->extra_spacing;
426 int n;
427 DiaObject *obj = &orth->object;
428 Rectangle rect;
429 Point *points;
431 points = &orth->points[0];
432 n = orth->numpoints;
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);
444 /* Boundingbox: */
445 extra->start_long =
446 extra->start_trans =
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);
455 static DiaObject *
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);
460 if (version == 0) {
461 AttributeNode attr;
462 /* In old objects with no autorouting, set it to false. */
463 attr = object_find_attribute(obj_node, "autorouting");
464 if (attr == NULL)
465 ((OrthConn*)obj)->autorouting = FALSE;
467 return obj;