1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
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.
8 *
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.
13 *
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.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <assert.h>
24 #include <math.h>
25 #include <string.h>
26
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"
33
34 #include "properties.h"
35
36 #include "stereotype.h"
37 #include "uml.h"
38
39 #include "pixmaps/dependency.xpm"
40
41 typedef struct _Dependency Dependency;
42
43 struct _Dependency {
44 OrthConn orth;
45
46 Point text_pos;
47 Alignment text_align;
48 real text_width;
49
50 Color text_color;
51 Color line_color;
52
53 int draw_arrow;
54 char *name;
55 char *stereotype; /* excluding << and >> */
56 char *st_stereotype; /* including << and >> */
57 };
58
59
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
65
66 static DiaFont *dep_font = NULL;
67
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);
83
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);
89
90 static void dependency_update_data(Dependency *dep);
91
92 static ObjectTypeOps dependency_type_ops =
93 {
94 (CreateFunc) dependency_create,
95 (LoadFunc) dependency_load,/*using_properties*/ /* load */
96 (SaveFunc) object_save_using_properties, /* save */
97 (GetDefaultsFunc) NULL,
98 (ApplyDefaultsFunc) NULL
99 };
100
101 DiaObjectType dependency_type =
102 {
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 */
107
108 &dependency_type_ops, /* ops */
109 NULL, /* pixmap_file */
110 0 /* default_user_data */
111 };
112
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 (ApplyPropertiesDialogFunc) 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,
127 (TextEditFunc) 0,
128 (ApplyPropertiesListFunc) object_apply_props,
129 };
130
131 static PropDescription dependency_props[] = {
132 ORTHCONN_COMMON_PROPERTIES,
133 /* 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 */
134 PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
135 PROP_STD_LINE_COLOUR_OPTIONAL,
136 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
137 N_("Name:"), NULL, NULL },
138 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
139 N_("Stereotype:"), NULL, NULL },
140 { "draw_arrow", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
141 N_("Show arrow:"), NULL, NULL },
142 PROP_DESC_END
143 };
144
145 static PropDescription *
dependency_describe_props(Dependency * dependency)146 dependency_describe_props(Dependency *dependency)
147 {
148 if (dependency_props[0].quark == 0) {
149 prop_desc_list_calculate_quarks(dependency_props);
150 }
151 return dependency_props;
152 }
153
154 static PropOffset dependency_offsets[] = {
155 ORTHCONN_COMMON_PROPERTIES_OFFSETS,
156 { "text_colour", PROP_TYPE_COLOUR, offsetof(Dependency, text_color) },
157 { "line_colour", PROP_TYPE_COLOUR, offsetof(Dependency, line_color) },
158 { "name", PROP_TYPE_STRING, offsetof(Dependency, name) },
159 { "stereotype", PROP_TYPE_STRING, offsetof(Dependency, stereotype) },
160 { "draw_arrow", PROP_TYPE_BOOL, offsetof(Dependency, draw_arrow) },
161 { NULL, 0, 0 }
162 };
163
164 static void
dependency_get_props(Dependency * dependency,GPtrArray * props)165 dependency_get_props(Dependency * dependency, GPtrArray *props)
166 {
167 object_get_props_from_offsets(&dependency->orth.object,
168 dependency_offsets,props);
169 }
170
171 static void
dependency_set_props(Dependency * dependency,GPtrArray * props)172 dependency_set_props(Dependency *dependency, GPtrArray *props)
173 {
174 object_set_props_from_offsets(&dependency->orth.object,
175 dependency_offsets, props);
176 g_free(dependency->st_stereotype);
177 dependency->st_stereotype = NULL;
178 dependency_update_data(dependency);
179 }
180
181 static real
dependency_distance_from(Dependency * dep,Point * point)182 dependency_distance_from(Dependency *dep, Point *point)
183 {
184 OrthConn *orth = &dep->orth;
185 return orthconn_distance_from(orth, point, DEPENDENCY_WIDTH);
186 }
187
188 static void
dependency_select(Dependency * dep,Point * clicked_point,DiaRenderer * interactive_renderer)189 dependency_select(Dependency *dep, Point *clicked_point,
190 DiaRenderer *interactive_renderer)
191 {
192 orthconn_update_data(&dep->orth);
193 }
194
195 static ObjectChange*
dependency_move_handle(Dependency * dep,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)196 dependency_move_handle(Dependency *dep, Handle *handle,
197 Point *to, ConnectionPoint *cp,
198 HandleMoveReason reason, ModifierKeys modifiers)
199 {
200 ObjectChange *change;
201 assert(dep!=NULL);
202 assert(handle!=NULL);
203 assert(to!=NULL);
204
205 change = orthconn_move_handle(&dep->orth, handle, to, cp, reason, modifiers);
206 dependency_update_data(dep);
207
208 return change;
209 }
210
211 static ObjectChange*
dependency_move(Dependency * dep,Point * to)212 dependency_move(Dependency *dep, Point *to)
213 {
214 ObjectChange *change;
215
216 change = orthconn_move(&dep->orth, to);
217 dependency_update_data(dep);
218
219 return change;
220 }
221
222 static void
dependency_draw(Dependency * dep,DiaRenderer * renderer)223 dependency_draw(Dependency *dep, DiaRenderer *renderer)
224 {
225 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
226 OrthConn *orth = &dep->orth;
227 Point *points;
228 int n;
229 Point pos;
230 Arrow arrow;
231
232 points = &orth->points[0];
233 n = orth->numpoints;
234
235 renderer_ops->set_linewidth(renderer, DEPENDENCY_WIDTH);
236 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
237 renderer_ops->set_dashlength(renderer, DEPENDENCY_DASHLEN);
238 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
239 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
240
241 arrow.type = ARROW_LINES;
242 arrow.length = DEPENDENCY_ARROWLEN;
243 arrow.width = DEPENDENCY_ARROWWIDTH;
244
245 renderer_ops->draw_polyline_with_arrows(renderer,
246 points, n,
247 DEPENDENCY_WIDTH,
248 &dep->line_color,
249 NULL, &arrow);
250
251 renderer_ops->set_font(renderer, dep_font, DEPENDENCY_FONTHEIGHT);
252 pos = dep->text_pos;
253
254 if (dep->st_stereotype != NULL && dep->st_stereotype[0] != '\0') {
255 renderer_ops->draw_string(renderer,
256 dep->st_stereotype,
257 &pos, dep->text_align,
258 &dep->text_color);
259
260 pos.y += DEPENDENCY_FONTHEIGHT;
261 }
262
263 if (dep->name != NULL && dep->name[0] != '\0') {
264 renderer_ops->draw_string(renderer,
265 dep->name,
266 &pos, dep->text_align,
267 &dep->text_color);
268 }
269
270 }
271
272 static void
dependency_update_data(Dependency * dep)273 dependency_update_data(Dependency *dep)
274 {
275 OrthConn *orth = &dep->orth;
276 DiaObject *obj = &orth->object;
277 PolyBBExtras *extra = &orth->extra_spacing;
278 int num_segm, i;
279 Point *points;
280 Rectangle rect;
281
282 orthconn_update_data(orth);
283
284 dep->stereotype = remove_stereotype_from_string(dep->stereotype);
285 if (!dep->st_stereotype) {
286 dep->st_stereotype = string_to_stereotype(dep->stereotype);
287 }
288
289 dep->text_width = 0.0;
290 if (dep->name)
291 dep->text_width = dia_font_string_width(dep->name, dep_font,
292 DEPENDENCY_FONTHEIGHT);
293 if (dep->stereotype)
294 dep->text_width = MAX(dep->text_width,
295 dia_font_string_width(dep->stereotype, dep_font,
296 DEPENDENCY_FONTHEIGHT));
297
298 extra->start_trans =
299 extra->start_long =
300 extra->middle_trans = DEPENDENCY_WIDTH/2.0;
301
302 extra->end_trans =
303 extra->end_long = (dep->draw_arrow?
304 (DEPENDENCY_WIDTH + DEPENDENCY_ARROWLEN)/2.0:
305 DEPENDENCY_WIDTH/2.0);
306
307 orthconn_update_boundingbox(orth);
308
309 /* Calc text pos: */
310 num_segm = dep->orth.numpoints - 1;
311 points = dep->orth.points;
312 i = num_segm / 2;
313
314 if ((num_segm % 2) == 0) { /* If no middle segment, use horizontal */
315 if (dep->orth.orientation[i]==VERTICAL)
316 i--;
317 }
318
319 switch (dep->orth.orientation[i]) {
320 case HORIZONTAL:
321 dep->text_align = ALIGN_CENTER;
322 dep->text_pos.x = 0.5*(points[i].x+points[i+1].x);
323 dep->text_pos.y = points[i].y;
324 if (dep->name)
325 dep->text_pos.y -= dia_font_descent(dep->name,
326 dep_font,
327 DEPENDENCY_FONTHEIGHT);
328 break;
329 case VERTICAL:
330 dep->text_align = ALIGN_LEFT;
331 dep->text_pos.x = points[i].x + 0.1;
332 dep->text_pos.y =
333 0.5*(points[i].y+points[i+1].y);
334 if (dep->name)
335 dep->text_pos.y -= dia_font_descent(dep->name,
336 dep_font,
337 DEPENDENCY_FONTHEIGHT);
338 break;
339 }
340
341 /* Add the text recangle to the bounding box: */
342 rect.left = dep->text_pos.x;
343 if (dep->text_align == ALIGN_CENTER)
344 rect.left -= dep->text_width/2.0;
345 rect.right = rect.left + dep->text_width;
346 rect.top = dep->text_pos.y;
347 if (dep->name)
348 rect.top -= dia_font_ascent(dep->name,
349 dep_font,
350 DEPENDENCY_FONTHEIGHT);
351 rect.bottom = rect.top + 2*DEPENDENCY_FONTHEIGHT;
352
353 rectangle_union(&obj->bounding_box, &rect);
354 }
355
356 static ObjectChange *
dependency_add_segment_callback(DiaObject * obj,Point * clicked,gpointer data)357 dependency_add_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
358 {
359 ObjectChange *change;
360 change = orthconn_add_segment((OrthConn *)obj, clicked);
361 dependency_update_data((Dependency *)obj);
362 return change;
363 }
364
365 static ObjectChange *
dependency_delete_segment_callback(DiaObject * obj,Point * clicked,gpointer data)366 dependency_delete_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
367 {
368 ObjectChange *change;
369 change = orthconn_delete_segment((OrthConn *)obj, clicked);
370 dependency_update_data((Dependency *)obj);
371 return change;
372 }
373
374
375 static DiaMenuItem object_menu_items[] = {
376 { N_("Add segment"), dependency_add_segment_callback, NULL, 1 },
377 { N_("Delete segment"), dependency_delete_segment_callback, NULL, 1 },
378 ORTHCONN_COMMON_MENUS,
379 };
380
381 static DiaMenu object_menu = {
382 "Dependency",
383 sizeof(object_menu_items)/sizeof(DiaMenuItem),
384 object_menu_items,
385 NULL
386 };
387
388 static DiaMenu *
dependency_get_object_menu(Dependency * dep,Point * clickedpoint)389 dependency_get_object_menu(Dependency *dep, Point *clickedpoint)
390 {
391 OrthConn *orth;
392
393 orth = &dep->orth;
394 /* Set entries sensitive/selected etc here */
395 object_menu_items[0].active = orthconn_can_add_segment(orth, clickedpoint);
396 object_menu_items[1].active = orthconn_can_delete_segment(orth, clickedpoint);
397 orthconn_update_object_menu(orth, clickedpoint, &object_menu_items[2]);
398
399 return &object_menu;
400 }
401
402 static DiaObject *
dependency_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)403 dependency_create(Point *startpoint,
404 void *user_data,
405 Handle **handle1,
406 Handle **handle2)
407 {
408 Dependency *dep;
409 OrthConn *orth;
410 DiaObject *obj;
411
412 if (dep_font == NULL) {
413 dep_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, DEPENDENCY_FONTHEIGHT);
414 }
415
416 dep = g_new0(Dependency, 1);
417 orth = &dep->orth;
418 obj = (DiaObject *)dep;
419
420 obj->type = &dependency_type;
421
422 obj->ops = &dependency_ops;
423
424 orthconn_init(orth, startpoint);
425
426 dep->text_color = color_black;
427 dep->line_color = attributes_get_foreground();
428 dep->draw_arrow = TRUE;
429 dep->name = NULL;
430 dep->stereotype = NULL;
431 dep->st_stereotype = NULL;
432 dep->text_width = 0;
433
434 dependency_update_data(dep);
435
436 *handle1 = orth->handles[0];
437 *handle2 = orth->handles[orth->numpoints-2];
438
439 return (DiaObject *)dep;
440 }
441
442 static void
dependency_destroy(Dependency * dep)443 dependency_destroy(Dependency *dep)
444 {
445 g_free(dep->name);
446 g_free(dep->stereotype);
447 g_free(dep->st_stereotype);
448
449 orthconn_destroy(&dep->orth);
450 }
451
452 static DiaObject *
dependency_load(ObjectNode obj_node,int version,const char * filename)453 dependency_load(ObjectNode obj_node, int version, const char *filename)
454 {
455 DiaObject *obj = object_load_using_properties(&dependency_type,
456 obj_node,version,filename);
457 if (version == 0) {
458 AttributeNode attr;
459 /* In old objects with no autorouting, set it to false. */
460 attr = object_find_attribute(obj_node, "autorouting");
461 if (attr == NULL)
462 ((OrthConn*)obj)->autorouting = FALSE;
463 }
464 return obj;
465 }
466
467
468