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.
29 #include "orth_conn.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
34 #include "properties.h"
36 #include "stereotype.h"
39 #include "pixmaps/dependency.xpm"
41 typedef struct _Dependency Dependency
;
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
,
80 static void dependency_destroy(Dependency
*dep
);
81 static DiaMenu
*dependency_get_object_menu(Dependency
*dep
,
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. */
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
},
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
) },
163 dependency_get_props(Dependency
* dependency
, GPtrArray
*props
)
165 object_get_props_from_offsets(&dependency
->orth
.object
,
166 dependency_offsets
,props
);
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
);
180 dependency_distance_from(Dependency
*dep
, Point
*point
)
182 OrthConn
*orth
= &dep
->orth
;
183 return orthconn_distance_from(orth
, point
, DEPENDENCY_WIDTH
);
187 dependency_select(Dependency
*dep
, Point
*clicked_point
,
188 DiaRenderer
*interactive_renderer
)
190 orthconn_update_data(&dep
->orth
);
194 dependency_move_handle(Dependency
*dep
, Handle
*handle
,
195 Point
*to
, ConnectionPoint
*cp
,
196 HandleMoveReason reason
, ModifierKeys modifiers
)
198 ObjectChange
*change
;
200 assert(handle
!=NULL
);
203 change
= orthconn_move_handle(&dep
->orth
, handle
, to
, cp
, reason
, modifiers
);
204 dependency_update_data(dep
);
210 dependency_move(Dependency
*dep
, Point
*to
)
212 ObjectChange
*change
;
214 change
= orthconn_move(&dep
->orth
, to
);
215 dependency_update_data(dep
);
221 dependency_draw(Dependency
*dep
, DiaRenderer
*renderer
)
223 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
224 OrthConn
*orth
= &dep
->orth
;
230 points
= &orth
->points
[0];
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
,
249 renderer_ops
->set_font(renderer
, dep_font
, DEPENDENCY_FONTHEIGHT
);
252 if (dep
->st_stereotype
!= NULL
&& dep
->st_stereotype
[0] != '\0') {
253 renderer_ops
->draw_string(renderer
,
255 &pos
, dep
->text_align
,
258 pos
.y
+= DEPENDENCY_FONTHEIGHT
;
261 if (dep
->name
!= NULL
&& dep
->name
[0] != '\0') {
262 renderer_ops
->draw_string(renderer
,
264 &pos
, dep
->text_align
,
271 dependency_update_data(Dependency
*dep
)
273 OrthConn
*orth
= &dep
->orth
;
274 DiaObject
*obj
= &orth
->object
;
275 PolyBBExtras
*extra
= &orth
->extra_spacing
;
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;
289 dep
->text_width
= dia_font_string_width(dep
->name
, dep_font
,
290 DEPENDENCY_FONTHEIGHT
);
292 dep
->text_width
= MAX(dep
->text_width
,
293 dia_font_string_width(dep
->stereotype
, dep_font
,
294 DEPENDENCY_FONTHEIGHT
));
298 extra
->middle_trans
= DEPENDENCY_WIDTH
/2.0;
301 extra
->end_long
= (dep
->draw_arrow
?
302 (DEPENDENCY_WIDTH
+ DEPENDENCY_ARROWLEN
)/2.0:
303 DEPENDENCY_WIDTH
/2.0);
305 orthconn_update_boundingbox(orth
);
308 num_segm
= dep
->orth
.numpoints
- 1;
309 points
= dep
->orth
.points
;
312 if ((num_segm
% 2) == 0) { /* If no middle segment, use horizontal */
313 if (dep
->orth
.orientation
[i
]==VERTICAL
)
317 switch (dep
->orth
.orientation
[i
]) {
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
;
323 dep
->text_pos
.y
-= dia_font_descent(dep
->name
,
325 DEPENDENCY_FONTHEIGHT
);
328 dep
->text_align
= ALIGN_LEFT
;
329 dep
->text_pos
.x
= points
[i
].x
+ 0.1;
331 0.5*(points
[i
].y
+points
[i
+1].y
);
333 dep
->text_pos
.y
-= dia_font_descent(dep
->name
,
335 DEPENDENCY_FONTHEIGHT
);
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
;
346 rect
.top
-= dia_font_ascent(dep
->name
,
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
);
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
);
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
= {
381 sizeof(object_menu_items
)/sizeof(DiaMenuItem
),
387 dependency_get_object_menu(Dependency
*dep
, Point
*clickedpoint
)
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]);
401 dependency_create(Point
*startpoint
,
410 if (dep_font
== NULL
) {
411 dep_font
= dia_font_new_from_style(DIA_FONT_MONOSPACE
, DEPENDENCY_FONTHEIGHT
);
414 dep
= g_new0(Dependency
, 1);
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
;
428 dep
->stereotype
= NULL
;
429 dep
->st_stereotype
= NULL
;
432 dependency_update_data(dep
);
434 *handle1
= orth
->handles
[0];
435 *handle2
= orth
->handles
[orth
->numpoints
-2];
437 return (DiaObject
*)dep
;
441 dependency_destroy(Dependency
*dep
)
444 g_free(dep
->stereotype
);
445 g_free(dep
->st_stereotype
);
447 orthconn_destroy(&dep
->orth
);
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
);
457 /* In old objects with no autorouting, set it to false. */
458 attr
= object_find_attribute(obj_node
, "autorouting");
460 ((OrthConn
*)obj
)->autorouting
= FALSE
;