2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / UML / dependency.c
blobae629aef0a71a4c120eaa597082b756a2b9b92a5
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <assert.h>
24 #include <math.h>
25 #include <string.h>
27 #include "intl.h"
28 #include "object.h"
29 #include "orth_conn.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
32 #include "arrows.h"
34 #include "properties.h"
36 #include "stereotype.h"
37 #include "uml.h"
39 #include "pixmaps/dependency.xpm"
41 typedef struct _Dependency Dependency;
43 struct _Dependency {
44 OrthConn orth;
46 Point text_pos;
47 Alignment text_align;
48 real text_width;
50 Color text_color;
51 Color line_color;
53 int draw_arrow;
54 char *name;
55 char *stereotype; /* excluding << and >> */
56 char *st_stereotype; /* including << and >> */
60 #define DEPENDENCY_WIDTH 0.1
61 #define DEPENDENCY_ARROWLEN 0.8
62 #define DEPENDENCY_ARROWWIDTH 0.5
63 #define DEPENDENCY_DASHLEN 0.4
64 #define DEPENDENCY_FONTHEIGHT 0.8
66 static DiaFont *dep_font = NULL;
68 static real dependency_distance_from(Dependency *dep, Point *point);
69 static void dependency_select(Dependency *dep, Point *clicked_point,
70 DiaRenderer *interactive_renderer);
71 static ObjectChange* dependency_move_handle(Dependency *dep, Handle *handle,
72 Point *to, ConnectionPoint *cp,
73 HandleMoveReason reason, ModifierKeys modifiers);
74 static ObjectChange* dependency_move(Dependency *dep, Point *to);
75 static void dependency_draw(Dependency *dep, DiaRenderer *renderer);
76 static DiaObject *dependency_create(Point *startpoint,
77 void *user_data,
78 Handle **handle1,
79 Handle **handle2);
80 static void dependency_destroy(Dependency *dep);
81 static DiaMenu *dependency_get_object_menu(Dependency *dep,
82 Point *clickedpoint);
84 static DiaObject *dependency_load(ObjectNode obj_node, int version,
85 const char *filename);
86 static PropDescription *dependency_describe_props(Dependency *dependency);
87 static void dependency_get_props(Dependency * dependency, GPtrArray *props);
88 static void dependency_set_props(Dependency * dependency, GPtrArray *props);
90 static void dependency_update_data(Dependency *dep);
92 static ObjectTypeOps dependency_type_ops =
94 (CreateFunc) dependency_create,
95 (LoadFunc) dependency_load,/*using_properties*/ /* load */
96 (SaveFunc) object_save_using_properties, /* save */
97 (GetDefaultsFunc) NULL,
98 (ApplyDefaultsFunc) NULL
101 DiaObjectType dependency_type =
103 "UML - Dependency", /* name */
104 /* Version 0 had no autorouting and so shouldn't have it set by default. */
105 1, /* version */
106 (char **) dependency_xpm, /* pixmap */
108 &dependency_type_ops, /* ops */
109 NULL, /* pixmap_file */
110 0 /* default_user_data */
113 static ObjectOps dependency_ops = {
114 (DestroyFunc) dependency_destroy,
115 (DrawFunc) dependency_draw,
116 (DistanceFunc) dependency_distance_from,
117 (SelectFunc) dependency_select,
118 (CopyFunc) object_copy_using_properties,
119 (MoveFunc) dependency_move,
120 (MoveHandleFunc) dependency_move_handle,
121 (GetPropertiesFunc) object_create_props_dialog,
122 (ApplyPropertiesFunc) object_apply_props_from_dialog,
123 (ObjectMenuFunc) dependency_get_object_menu,
124 (DescribePropsFunc) dependency_describe_props,
125 (GetPropsFunc) dependency_get_props,
126 (SetPropsFunc) dependency_set_props
129 static PropDescription dependency_props[] = {
130 ORTHCONN_COMMON_PROPERTIES,
131 /* can't use PROP_STD_TEXT_COLOUR_OPTIONAL cause it has PROP_FLAG_DONT_SAVE. It is designed to fill the Text object - not some subset */
132 PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
133 PROP_STD_LINE_COLOUR_OPTIONAL,
134 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
135 N_("Name:"), NULL, NULL },
136 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
137 N_("Stereotype:"), NULL, NULL },
138 { "draw_arrow", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
139 N_("Show arrow:"), NULL, NULL },
140 PROP_DESC_END
143 static PropDescription *
144 dependency_describe_props(Dependency *dependency)
146 if (dependency_props[0].quark == 0) {
147 prop_desc_list_calculate_quarks(dependency_props);
149 return dependency_props;
152 static PropOffset dependency_offsets[] = {
153 ORTHCONN_COMMON_PROPERTIES_OFFSETS,
154 { "text_colour", PROP_TYPE_COLOUR, offsetof(Dependency, text_color) },
155 { "line_colour", PROP_TYPE_COLOUR, offsetof(Dependency, line_color) },
156 { "name", PROP_TYPE_STRING, offsetof(Dependency, name) },
157 { "stereotype", PROP_TYPE_STRING, offsetof(Dependency, stereotype) },
158 { "draw_arrow", PROP_TYPE_BOOL, offsetof(Dependency, draw_arrow) },
159 { NULL, 0, 0 }
162 static void
163 dependency_get_props(Dependency * dependency, GPtrArray *props)
165 object_get_props_from_offsets(&dependency->orth.object,
166 dependency_offsets,props);
169 static void
170 dependency_set_props(Dependency *dependency, GPtrArray *props)
172 object_set_props_from_offsets(&dependency->orth.object,
173 dependency_offsets, props);
174 g_free(dependency->st_stereotype);
175 dependency->st_stereotype = NULL;
176 dependency_update_data(dependency);
179 static real
180 dependency_distance_from(Dependency *dep, Point *point)
182 OrthConn *orth = &dep->orth;
183 return orthconn_distance_from(orth, point, DEPENDENCY_WIDTH);
186 static void
187 dependency_select(Dependency *dep, Point *clicked_point,
188 DiaRenderer *interactive_renderer)
190 orthconn_update_data(&dep->orth);
193 static ObjectChange*
194 dependency_move_handle(Dependency *dep, Handle *handle,
195 Point *to, ConnectionPoint *cp,
196 HandleMoveReason reason, ModifierKeys modifiers)
198 ObjectChange *change;
199 assert(dep!=NULL);
200 assert(handle!=NULL);
201 assert(to!=NULL);
203 change = orthconn_move_handle(&dep->orth, handle, to, cp, reason, modifiers);
204 dependency_update_data(dep);
206 return change;
209 static ObjectChange*
210 dependency_move(Dependency *dep, Point *to)
212 ObjectChange *change;
214 change = orthconn_move(&dep->orth, to);
215 dependency_update_data(dep);
217 return change;
220 static void
221 dependency_draw(Dependency *dep, DiaRenderer *renderer)
223 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
224 OrthConn *orth = &dep->orth;
225 Point *points;
226 int n;
227 Point pos;
228 Arrow arrow;
230 points = &orth->points[0];
231 n = orth->numpoints;
233 renderer_ops->set_linewidth(renderer, DEPENDENCY_WIDTH);
234 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
235 renderer_ops->set_dashlength(renderer, DEPENDENCY_DASHLEN);
236 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
237 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
239 arrow.type = ARROW_LINES;
240 arrow.length = DEPENDENCY_ARROWLEN;
241 arrow.width = DEPENDENCY_ARROWWIDTH;
243 renderer_ops->draw_polyline_with_arrows(renderer,
244 points, n,
245 DEPENDENCY_WIDTH,
246 &dep->line_color,
247 NULL, &arrow);
249 renderer_ops->set_font(renderer, dep_font, DEPENDENCY_FONTHEIGHT);
250 pos = dep->text_pos;
252 if (dep->st_stereotype != NULL && dep->st_stereotype[0] != '\0') {
253 renderer_ops->draw_string(renderer,
254 dep->st_stereotype,
255 &pos, dep->text_align,
256 &dep->text_color);
258 pos.y += DEPENDENCY_FONTHEIGHT;
261 if (dep->name != NULL && dep->name[0] != '\0') {
262 renderer_ops->draw_string(renderer,
263 dep->name,
264 &pos, dep->text_align,
265 &dep->text_color);
270 static void
271 dependency_update_data(Dependency *dep)
273 OrthConn *orth = &dep->orth;
274 DiaObject *obj = &orth->object;
275 PolyBBExtras *extra = &orth->extra_spacing;
276 int num_segm, i;
277 Point *points;
278 Rectangle rect;
280 orthconn_update_data(orth);
282 dep->stereotype = remove_stereotype_from_string(dep->stereotype);
283 if (!dep->st_stereotype) {
284 dep->st_stereotype = string_to_stereotype(dep->stereotype);
287 dep->text_width = 0.0;
288 if (dep->name)
289 dep->text_width = dia_font_string_width(dep->name, dep_font,
290 DEPENDENCY_FONTHEIGHT);
291 if (dep->stereotype)
292 dep->text_width = MAX(dep->text_width,
293 dia_font_string_width(dep->stereotype, dep_font,
294 DEPENDENCY_FONTHEIGHT));
296 extra->start_trans =
297 extra->start_long =
298 extra->middle_trans = DEPENDENCY_WIDTH/2.0;
300 extra->end_trans =
301 extra->end_long = (dep->draw_arrow?
302 (DEPENDENCY_WIDTH + DEPENDENCY_ARROWLEN)/2.0:
303 DEPENDENCY_WIDTH/2.0);
305 orthconn_update_boundingbox(orth);
307 /* Calc text pos: */
308 num_segm = dep->orth.numpoints - 1;
309 points = dep->orth.points;
310 i = num_segm / 2;
312 if ((num_segm % 2) == 0) { /* If no middle segment, use horizontal */
313 if (dep->orth.orientation[i]==VERTICAL)
314 i--;
317 switch (dep->orth.orientation[i]) {
318 case HORIZONTAL:
319 dep->text_align = ALIGN_CENTER;
320 dep->text_pos.x = 0.5*(points[i].x+points[i+1].x);
321 dep->text_pos.y = points[i].y;
322 if (dep->name)
323 dep->text_pos.y -= dia_font_descent(dep->name,
324 dep_font,
325 DEPENDENCY_FONTHEIGHT);
326 break;
327 case VERTICAL:
328 dep->text_align = ALIGN_LEFT;
329 dep->text_pos.x = points[i].x + 0.1;
330 dep->text_pos.y =
331 0.5*(points[i].y+points[i+1].y);
332 if (dep->name)
333 dep->text_pos.y -= dia_font_descent(dep->name,
334 dep_font,
335 DEPENDENCY_FONTHEIGHT);
336 break;
339 /* Add the text recangle to the bounding box: */
340 rect.left = dep->text_pos.x;
341 if (dep->text_align == ALIGN_CENTER)
342 rect.left -= dep->text_width/2.0;
343 rect.right = rect.left + dep->text_width;
344 rect.top = dep->text_pos.y;
345 if (dep->name)
346 rect.top -= dia_font_ascent(dep->name,
347 dep_font,
348 DEPENDENCY_FONTHEIGHT);
349 rect.bottom = rect.top + 2*DEPENDENCY_FONTHEIGHT;
351 rectangle_union(&obj->bounding_box, &rect);
354 static ObjectChange *
355 dependency_add_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
357 ObjectChange *change;
358 change = orthconn_add_segment((OrthConn *)obj, clicked);
359 dependency_update_data((Dependency *)obj);
360 return change;
363 static ObjectChange *
364 dependency_delete_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
366 ObjectChange *change;
367 change = orthconn_delete_segment((OrthConn *)obj, clicked);
368 dependency_update_data((Dependency *)obj);
369 return change;
373 static DiaMenuItem object_menu_items[] = {
374 { N_("Add segment"), dependency_add_segment_callback, NULL, 1 },
375 { N_("Delete segment"), dependency_delete_segment_callback, NULL, 1 },
376 ORTHCONN_COMMON_MENUS,
379 static DiaMenu object_menu = {
380 "Dependency",
381 sizeof(object_menu_items)/sizeof(DiaMenuItem),
382 object_menu_items,
383 NULL
386 static DiaMenu *
387 dependency_get_object_menu(Dependency *dep, Point *clickedpoint)
389 OrthConn *orth;
391 orth = &dep->orth;
392 /* Set entries sensitive/selected etc here */
393 object_menu_items[0].active = orthconn_can_add_segment(orth, clickedpoint);
394 object_menu_items[1].active = orthconn_can_delete_segment(orth, clickedpoint);
395 orthconn_update_object_menu(orth, clickedpoint, &object_menu_items[2]);
397 return &object_menu;
400 static DiaObject *
401 dependency_create(Point *startpoint,
402 void *user_data,
403 Handle **handle1,
404 Handle **handle2)
406 Dependency *dep;
407 OrthConn *orth;
408 DiaObject *obj;
410 if (dep_font == NULL) {
411 dep_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, DEPENDENCY_FONTHEIGHT);
414 dep = g_new0(Dependency, 1);
415 orth = &dep->orth;
416 obj = (DiaObject *)dep;
418 obj->type = &dependency_type;
420 obj->ops = &dependency_ops;
422 orthconn_init(orth, startpoint);
424 dep->text_color = color_black;
425 dep->line_color = attributes_get_foreground();
426 dep->draw_arrow = TRUE;
427 dep->name = NULL;
428 dep->stereotype = NULL;
429 dep->st_stereotype = NULL;
430 dep->text_width = 0;
432 dependency_update_data(dep);
434 *handle1 = orth->handles[0];
435 *handle2 = orth->handles[orth->numpoints-2];
437 return (DiaObject *)dep;
440 static void
441 dependency_destroy(Dependency *dep)
443 g_free(dep->name);
444 g_free(dep->stereotype);
445 g_free(dep->st_stereotype);
447 orthconn_destroy(&dep->orth);
450 static DiaObject *
451 dependency_load(ObjectNode obj_node, int version, const char *filename)
453 DiaObject *obj = object_load_using_properties(&dependency_type,
454 obj_node,version,filename);
455 if (version == 0) {
456 AttributeNode attr;
457 /* In old objects with no autorouting, set it to false. */
458 attr = object_find_attribute(obj_node, "autorouting");
459 if (attr == NULL)
460 ((OrthConn*)obj)->autorouting = FALSE;
462 return obj;