Template rendering improvements.
[dia.git] / objects / UML / dependency.c
blobe724ed5fc0536238c308dc6d8f229dccdbd8f52e
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 PROP_STD_TEXT_COLOUR_OPTIONAL,
132 PROP_STD_LINE_COLOUR_OPTIONAL,
133 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
134 N_("Name:"), NULL, NULL },
135 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
136 N_("Stereotype:"), NULL, NULL },
137 { "draw_arrow", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
138 N_("Show arrow:"), NULL, NULL },
139 PROP_DESC_END
142 static PropDescription *
143 dependency_describe_props(Dependency *dependency)
145 if (dependency_props[0].quark == 0) {
146 prop_desc_list_calculate_quarks(dependency_props);
148 return dependency_props;
151 static PropOffset dependency_offsets[] = {
152 ORTHCONN_COMMON_PROPERTIES_OFFSETS,
153 { "text_colour", PROP_TYPE_COLOUR, offsetof(Dependency, text_color) },
154 { "line_colour", PROP_TYPE_COLOUR, offsetof(Dependency, line_color) },
155 { "name", PROP_TYPE_STRING, offsetof(Dependency, name) },
156 { "stereotype", PROP_TYPE_STRING, offsetof(Dependency, stereotype) },
157 { "draw_arrow", PROP_TYPE_BOOL, offsetof(Dependency, draw_arrow) },
158 { NULL, 0, 0 }
161 static void
162 dependency_get_props(Dependency * dependency, GPtrArray *props)
164 object_get_props_from_offsets(&dependency->orth.object,
165 dependency_offsets,props);
168 static void
169 dependency_set_props(Dependency *dependency, GPtrArray *props)
171 object_set_props_from_offsets(&dependency->orth.object,
172 dependency_offsets, props);
173 g_free(dependency->st_stereotype);
174 dependency->st_stereotype = NULL;
175 dependency_update_data(dependency);
178 static real
179 dependency_distance_from(Dependency *dep, Point *point)
181 OrthConn *orth = &dep->orth;
182 return orthconn_distance_from(orth, point, DEPENDENCY_WIDTH);
185 static void
186 dependency_select(Dependency *dep, Point *clicked_point,
187 DiaRenderer *interactive_renderer)
189 orthconn_update_data(&dep->orth);
192 static ObjectChange*
193 dependency_move_handle(Dependency *dep, Handle *handle,
194 Point *to, ConnectionPoint *cp,
195 HandleMoveReason reason, ModifierKeys modifiers)
197 ObjectChange *change;
198 assert(dep!=NULL);
199 assert(handle!=NULL);
200 assert(to!=NULL);
202 change = orthconn_move_handle(&dep->orth, handle, to, cp, reason, modifiers);
203 dependency_update_data(dep);
205 return change;
208 static ObjectChange*
209 dependency_move(Dependency *dep, Point *to)
211 ObjectChange *change;
213 change = orthconn_move(&dep->orth, to);
214 dependency_update_data(dep);
216 return change;
219 static void
220 dependency_draw(Dependency *dep, DiaRenderer *renderer)
222 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
223 OrthConn *orth = &dep->orth;
224 Point *points;
225 int n;
226 Point pos;
227 Arrow arrow;
229 points = &orth->points[0];
230 n = orth->numpoints;
232 renderer_ops->set_linewidth(renderer, DEPENDENCY_WIDTH);
233 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
234 renderer_ops->set_dashlength(renderer, DEPENDENCY_DASHLEN);
235 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
236 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
238 arrow.type = ARROW_LINES;
239 arrow.length = DEPENDENCY_ARROWLEN;
240 arrow.width = DEPENDENCY_ARROWWIDTH;
242 renderer_ops->draw_polyline_with_arrows(renderer,
243 points, n,
244 DEPENDENCY_WIDTH,
245 &dep->line_color,
246 NULL, &arrow);
248 renderer_ops->set_font(renderer, dep_font, DEPENDENCY_FONTHEIGHT);
249 pos = dep->text_pos;
251 if (dep->st_stereotype != NULL && dep->st_stereotype[0] != '\0') {
252 renderer_ops->draw_string(renderer,
253 dep->st_stereotype,
254 &pos, dep->text_align,
255 &dep->text_color);
257 pos.y += DEPENDENCY_FONTHEIGHT;
260 if (dep->name != NULL && dep->name[0] != '\0') {
261 renderer_ops->draw_string(renderer,
262 dep->name,
263 &pos, dep->text_align,
264 &dep->line_color);
269 static void
270 dependency_update_data(Dependency *dep)
272 OrthConn *orth = &dep->orth;
273 DiaObject *obj = &orth->object;
274 PolyBBExtras *extra = &orth->extra_spacing;
275 int num_segm, i;
276 Point *points;
277 Rectangle rect;
279 orthconn_update_data(orth);
281 dep->stereotype = remove_stereotype_from_string(dep->stereotype);
282 if (!dep->st_stereotype) {
283 dep->st_stereotype = string_to_stereotype(dep->stereotype);
286 dep->text_width = 0.0;
287 if (dep->name)
288 dep->text_width = dia_font_string_width(dep->name, dep_font,
289 DEPENDENCY_FONTHEIGHT);
290 if (dep->stereotype)
291 dep->text_width = MAX(dep->text_width,
292 dia_font_string_width(dep->stereotype, dep_font,
293 DEPENDENCY_FONTHEIGHT));
295 extra->start_trans =
296 extra->start_long =
297 extra->middle_trans = DEPENDENCY_WIDTH/2.0;
299 extra->end_trans =
300 extra->end_long = (dep->draw_arrow?
301 (DEPENDENCY_WIDTH + DEPENDENCY_ARROWLEN)/2.0:
302 DEPENDENCY_WIDTH/2.0);
304 orthconn_update_boundingbox(orth);
306 /* Calc text pos: */
307 num_segm = dep->orth.numpoints - 1;
308 points = dep->orth.points;
309 i = num_segm / 2;
311 if ((num_segm % 2) == 0) { /* If no middle segment, use horizontal */
312 if (dep->orth.orientation[i]==VERTICAL)
313 i--;
316 switch (dep->orth.orientation[i]) {
317 case HORIZONTAL:
318 dep->text_align = ALIGN_CENTER;
319 dep->text_pos.x = 0.5*(points[i].x+points[i+1].x);
320 dep->text_pos.y = points[i].y;
321 if (dep->name)
322 dep->text_pos.y -= dia_font_descent(dep->name,
323 dep_font,
324 DEPENDENCY_FONTHEIGHT);
325 break;
326 case VERTICAL:
327 dep->text_align = ALIGN_LEFT;
328 dep->text_pos.x = points[i].x + 0.1;
329 dep->text_pos.y =
330 0.5*(points[i].y+points[i+1].y);
331 if (dep->name)
332 dep->text_pos.y -= dia_font_descent(dep->name,
333 dep_font,
334 DEPENDENCY_FONTHEIGHT);
335 break;
338 /* Add the text recangle to the bounding box: */
339 rect.left = dep->text_pos.x;
340 if (dep->text_align == ALIGN_CENTER)
341 rect.left -= dep->text_width/2.0;
342 rect.right = rect.left + dep->text_width;
343 rect.top = dep->text_pos.y;
344 if (dep->name)
345 rect.top -= dia_font_ascent(dep->name,
346 dep_font,
347 DEPENDENCY_FONTHEIGHT);
348 rect.bottom = rect.top + 2*DEPENDENCY_FONTHEIGHT;
350 rectangle_union(&obj->bounding_box, &rect);
353 static ObjectChange *
354 dependency_add_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
356 ObjectChange *change;
357 change = orthconn_add_segment((OrthConn *)obj, clicked);
358 dependency_update_data((Dependency *)obj);
359 return change;
362 static ObjectChange *
363 dependency_delete_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
365 ObjectChange *change;
366 change = orthconn_delete_segment((OrthConn *)obj, clicked);
367 dependency_update_data((Dependency *)obj);
368 return change;
372 static DiaMenuItem object_menu_items[] = {
373 { N_("Add segment"), dependency_add_segment_callback, NULL, 1 },
374 { N_("Delete segment"), dependency_delete_segment_callback, NULL, 1 },
375 ORTHCONN_COMMON_MENUS,
378 static DiaMenu object_menu = {
379 "Dependency",
380 sizeof(object_menu_items)/sizeof(DiaMenuItem),
381 object_menu_items,
382 NULL
385 static DiaMenu *
386 dependency_get_object_menu(Dependency *dep, Point *clickedpoint)
388 OrthConn *orth;
390 orth = &dep->orth;
391 /* Set entries sensitive/selected etc here */
392 object_menu_items[0].active = orthconn_can_add_segment(orth, clickedpoint);
393 object_menu_items[1].active = orthconn_can_delete_segment(orth, clickedpoint);
394 orthconn_update_object_menu(orth, clickedpoint, &object_menu_items[2]);
396 return &object_menu;
399 static DiaObject *
400 dependency_create(Point *startpoint,
401 void *user_data,
402 Handle **handle1,
403 Handle **handle2)
405 Dependency *dep;
406 OrthConn *orth;
407 DiaObject *obj;
409 if (dep_font == NULL) {
410 dep_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, DEPENDENCY_FONTHEIGHT);
413 dep = g_new0(Dependency, 1);
414 orth = &dep->orth;
415 obj = (DiaObject *)dep;
417 obj->type = &dependency_type;
419 obj->ops = &dependency_ops;
421 orthconn_init(orth, startpoint);
423 dep->text_color = color_black;
424 dep->line_color = attributes_get_foreground();
425 dep->draw_arrow = TRUE;
426 dep->name = NULL;
427 dep->stereotype = NULL;
428 dep->st_stereotype = NULL;
429 dep->text_width = 0;
431 dependency_update_data(dep);
433 *handle1 = orth->handles[0];
434 *handle2 = orth->handles[orth->numpoints-2];
436 return (DiaObject *)dep;
439 static void
440 dependency_destroy(Dependency *dep)
442 g_free(dep->name);
443 g_free(dep->stereotype);
444 g_free(dep->st_stereotype);
446 orthconn_destroy(&dep->orth);
449 static DiaObject *
450 dependency_load(ObjectNode obj_node, int version, const char *filename)
452 DiaObject *obj = object_load_using_properties(&dependency_type,
453 obj_node,version,filename);
454 if (version == 0) {
455 AttributeNode attr;
456 /* In old objects with no autorouting, set it to false. */
457 attr = object_find_attribute(obj_node, "autorouting");
458 if (attr == NULL)
459 ((OrthConn*)obj)->autorouting = FALSE;
461 return obj;