2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / objects / UML / message.c
blobb6e60423772e39c2286ffac3c64becaa9b3aa9a5
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 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>
26 #include <stdio.h>
28 #include <glib.h>
30 #include "intl.h"
31 #include "object.h"
32 #include "connection.h"
33 #include "diarenderer.h"
34 #include "handle.h"
35 #include "arrows.h"
36 #include "properties.h"
37 #include "attributes.h"
39 #include "pixmaps/message.xpm"
41 #include "uml.h"
43 typedef struct _Message Message;
45 typedef enum {
46 MESSAGE_CALL,
47 MESSAGE_CREATE,
48 MESSAGE_DESTROY,
49 MESSAGE_SIMPLE,
50 MESSAGE_RETURN,
51 MESSAGE_SEND, /* Asynchronous */
52 MESSAGE_RECURSIVE
53 } MessageType;
55 struct _Message {
56 Connection connection;
58 Handle text_handle;
60 gchar *text;
61 Point text_pos;
62 real text_width;
64 Color text_color;
65 Color line_color;
67 MessageType type;
70 #define MESSAGE_WIDTH 0.1
71 #define MESSAGE_DASHLEN 0.4
72 #define MESSAGE_FONTHEIGHT 0.8
73 #define MESSAGE_ARROWLEN 0.8
74 #define MESSAGE_ARROWWIDTH 0.5
75 #define HANDLE_MOVE_TEXT (HANDLE_CUSTOM1)
78 static DiaFont *message_font = NULL;
80 static ObjectChange* message_move_handle(Message *message, Handle *handle,
81 Point *to, ConnectionPoint *cp,
82 HandleMoveReason reason, ModifierKeys modifiers);
83 static ObjectChange* message_move(Message *message, Point *to);
84 static void message_select(Message *message, Point *clicked_point,
85 DiaRenderer *interactive_renderer);
86 static void message_draw(Message *message, DiaRenderer *renderer);
87 static DiaObject *message_create(Point *startpoint,
88 void *user_data,
89 Handle **handle1,
90 Handle **handle2);
91 static real message_distance_from(Message *message, Point *point);
92 static void message_update_data(Message *message);
93 static void message_destroy(Message *message);
94 static DiaObject *message_load(ObjectNode obj_node, int version,
95 const char *filename);
97 static PropDescription *message_describe_props(Message *mes);
98 static void message_get_props(Message * message, GPtrArray *props);
99 static void message_set_props(Message *message, GPtrArray *props);
101 static ObjectTypeOps message_type_ops =
103 (CreateFunc) message_create,
104 (LoadFunc) message_load,/*using_properties*/ /* load */
105 (SaveFunc) object_save_using_properties, /* save */
106 (GetDefaultsFunc) NULL,
107 (ApplyDefaultsFunc) NULL
110 DiaObjectType message_type =
112 "UML - Message", /* name */
113 0, /* version */
114 (char **) message_xpm, /* pixmap */
115 &message_type_ops /* ops */
118 static ObjectOps message_ops = {
119 (DestroyFunc) message_destroy,
120 (DrawFunc) message_draw,
121 (DistanceFunc) message_distance_from,
122 (SelectFunc) message_select,
123 (CopyFunc) object_copy_using_properties,
124 (MoveFunc) message_move,
125 (MoveHandleFunc) message_move_handle,
126 (GetPropertiesFunc) object_create_props_dialog,
127 (ApplyPropertiesFunc) object_apply_props_from_dialog,
128 (ObjectMenuFunc) NULL,
129 (DescribePropsFunc) message_describe_props,
130 (GetPropsFunc) message_get_props,
131 (SetPropsFunc) message_set_props
134 static PropEnumData prop_message_type_data[] = {
135 { N_("Call"), MESSAGE_CALL },
136 { N_("Create"), MESSAGE_CREATE },
137 { N_("Destroy"), MESSAGE_DESTROY },
138 { N_("Simple"), MESSAGE_SIMPLE },
139 { N_("Return"), MESSAGE_RETURN },
140 { N_("Send"), MESSAGE_SEND },
141 { N_("Recursive"), MESSAGE_RECURSIVE },
142 { NULL, 0}
145 static PropDescription message_props[] = {
146 CONNECTION_COMMON_PROPERTIES,
147 /* 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 */
148 PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
149 PROP_STD_LINE_COLOUR_OPTIONAL,
150 { "text", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
151 N_("Message:"), NULL, NULL },
152 { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
153 N_("Message type:"), NULL, prop_message_type_data },
154 { "text_pos", PROP_TYPE_POINT, 0,
155 "text_pos:", NULL,NULL },
156 PROP_DESC_END
159 static PropDescription *
160 message_describe_props(Message *mes)
162 if (message_props[0].quark == 0)
163 prop_desc_list_calculate_quarks(message_props);
164 return message_props;
168 static PropOffset message_offsets[] = {
169 CONNECTION_COMMON_PROPERTIES_OFFSETS,
170 { "line_colour",PROP_TYPE_COLOUR,offsetof(Message,line_color) },
171 { "text_colour",PROP_TYPE_COLOUR,offsetof(Message,text_color) },
172 { "text", PROP_TYPE_STRING, offsetof(Message, text) },
173 { "type", PROP_TYPE_ENUM, offsetof(Message, type) },
174 { "text_pos", PROP_TYPE_POINT, offsetof(Message,text_pos) },
175 { NULL, 0, 0 }
178 static void
179 message_get_props(Message * message, GPtrArray *props)
181 object_get_props_from_offsets(&message->connection.object,
182 message_offsets, props);
185 static void
186 message_set_props(Message *message, GPtrArray *props)
188 object_set_props_from_offsets(&message->connection.object,
189 message_offsets, props);
190 message_update_data(message);
194 static real
195 message_distance_from(Message *message, Point *point)
197 Point *endpoints;
198 real dist;
200 endpoints = &message->connection.endpoints[0];
202 dist = distance_line_point(&endpoints[0], &endpoints[1], MESSAGE_WIDTH, point);
204 return dist;
207 static void
208 message_select(Message *message, Point *clicked_point,
209 DiaRenderer *interactive_renderer)
211 connection_update_handles(&message->connection);
214 static ObjectChange*
215 message_move_handle(Message *message, Handle *handle,
216 Point *to, ConnectionPoint *cp,
217 HandleMoveReason reason, ModifierKeys modifiers)
219 Point p1, p2;
220 Point *endpoints;
222 assert(message!=NULL);
223 assert(handle!=NULL);
224 assert(to!=NULL);
226 if (handle->id == HANDLE_MOVE_TEXT) {
227 message->text_pos = *to;
228 } else {
229 endpoints = &message->connection.endpoints[0];
230 p1.x = 0.5*(endpoints[0].x + endpoints[1].x);
231 p1.y = 0.5*(endpoints[0].y + endpoints[1].y);
232 connection_move_handle(&message->connection, handle->id, to, cp, reason, modifiers);
233 p2.x = 0.5*(endpoints[0].x + endpoints[1].x);
234 p2.y = 0.5*(endpoints[0].y + endpoints[1].y);
235 point_sub(&p2, &p1);
236 point_add(&message->text_pos, &p2);
239 message_update_data(message);
241 return NULL;
244 static ObjectChange*
245 message_move(Message *message, Point *to)
247 Point start_to_end;
248 Point *endpoints = &message->connection.endpoints[0];
249 Point delta;
251 delta = *to;
252 point_sub(&delta, &endpoints[0]);
254 start_to_end = endpoints[1];
255 point_sub(&start_to_end, &endpoints[0]);
257 endpoints[1] = endpoints[0] = *to;
258 point_add(&endpoints[1], &start_to_end);
260 point_add(&message->text_pos, &delta);
262 message_update_data(message);
264 return NULL;
267 static void
268 message_draw(Message *message, DiaRenderer *renderer)
270 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
271 Point *endpoints, p1, p2, px;
272 Arrow arrow;
273 int n1 = 1, n2 = 0;
274 gchar *mname = NULL;
276 assert(message != NULL);
277 assert(renderer != NULL);
279 if (message->type==MESSAGE_SEND)
280 arrow.type = ARROW_HALF_HEAD;
281 else if (message->type==MESSAGE_SIMPLE)
282 arrow.type = ARROW_LINES;
283 else
284 arrow.type = ARROW_FILLED_TRIANGLE;
285 arrow.length = MESSAGE_ARROWLEN;
286 arrow.width = MESSAGE_ARROWWIDTH;
288 endpoints = &message->connection.endpoints[0];
290 renderer_ops->set_linewidth(renderer, MESSAGE_WIDTH);
292 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
294 if (message->type==MESSAGE_RECURSIVE) {
295 n1 = 0;
296 n2 = 1;
299 if (message->type==MESSAGE_RETURN) {
300 renderer_ops->set_dashlength(renderer, MESSAGE_DASHLEN);
301 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
302 n1 = 0;
303 n2 = 1;
304 } else
305 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
307 p1 = endpoints[n1];
308 p2 = endpoints[n2];
310 if (message->type==MESSAGE_RECURSIVE) {
311 px.x = p2.x;
312 px.y = p1.y;
313 renderer_ops->draw_line(renderer,
314 &p1, &px,
315 &message->line_color);
317 renderer_ops->draw_line(renderer,
318 &px, &p2,
319 &message->line_color);
320 p1.y = p2.y;
323 renderer_ops->draw_line_with_arrows(renderer,
324 &p1, &p2,
325 MESSAGE_WIDTH,
326 &message->line_color,
327 &arrow, NULL);
329 renderer_ops->set_font(renderer, message_font,
330 MESSAGE_FONTHEIGHT);
332 if (message->type==MESSAGE_CREATE)
333 mname = g_strdup_printf ("%s%s%s", UML_STEREOTYPE_START, "create", UML_STEREOTYPE_END);
334 else if (message->type==MESSAGE_DESTROY)
335 mname = g_strdup_printf ("%s%s%s", UML_STEREOTYPE_START, "destroy", UML_STEREOTYPE_END);
336 else
337 mname = message->text;
339 if (mname && strlen(mname) != 0)
340 renderer_ops->draw_string(renderer,
341 mname, /*message->text,*/
342 &message->text_pos, ALIGN_CENTER,
343 &message->text_color);
344 if (message->type == MESSAGE_CREATE || message->type == MESSAGE_DESTROY)
346 g_free(mname);
352 static DiaObject *
353 message_create(Point *startpoint,
354 void *user_data,
355 Handle **handle1,
356 Handle **handle2)
358 Message *message;
359 Connection *conn;
360 LineBBExtras *extra;
361 DiaObject *obj;
363 if (message_font == NULL) {
364 message_font =
365 dia_font_new_from_style (DIA_FONT_SANS, MESSAGE_FONTHEIGHT);
368 message = g_malloc0(sizeof(Message));
370 conn = &message->connection;
371 conn->endpoints[0] = *startpoint;
372 conn->endpoints[1] = *startpoint;
373 conn->endpoints[1].x += 1.5;
375 obj = &conn->object;
376 extra = &conn->extra_spacing;
378 obj->type = &message_type;
379 obj->ops = &message_ops;
381 connection_init(conn, 3, 0);
383 message->text_color = color_black;
384 message->line_color = attributes_get_foreground();
385 message->text = g_strdup("");
386 message->text_width = 0.0;
387 message->text_pos.x = 0.5*(conn->endpoints[0].x + conn->endpoints[1].x);
388 message->text_pos.y = 0.5*(conn->endpoints[0].y + conn->endpoints[1].y) + 0.5;
390 message->text_handle.id = HANDLE_MOVE_TEXT;
391 message->text_handle.type = HANDLE_MINOR_CONTROL;
392 message->text_handle.connect_type = HANDLE_NONCONNECTABLE;
393 message->text_handle.connected_to = NULL;
394 obj->handles[2] = &message->text_handle;
396 extra->start_long =
397 extra->start_trans =
398 extra->end_long = MESSAGE_WIDTH/2.0;
399 extra->end_trans = MAX(MESSAGE_WIDTH,MESSAGE_ARROWLEN)/2.0;
401 message_update_data(message);
403 *handle1 = obj->handles[0];
404 *handle2 = obj->handles[1];
405 return &message->connection.object;
408 static void
409 message_destroy(Message *message)
411 connection_destroy(&message->connection);
413 g_free(message->text);
416 static void
417 message_update_data(Message *message)
419 Connection *conn = &message->connection;
420 DiaObject *obj = &conn->object;
421 Rectangle rect;
423 if (connpoint_is_autogap(conn->endpoint_handles[0].connected_to) ||
424 connpoint_is_autogap(conn->endpoint_handles[1].connected_to)) {
425 connection_adjust_for_autogap(conn);
427 obj->position = conn->endpoints[0];
429 message->text_handle.pos = message->text_pos;
431 connection_update_handles(conn);
432 connection_update_boundingbox(conn);
434 message->text_width =
435 dia_font_string_width(message->text, message_font, MESSAGE_FONTHEIGHT);
437 /* Add boundingbox for text: */
438 rect.left = message->text_pos.x-message->text_width/2;
439 rect.right = rect.left + message->text_width;
440 rect.top = message->text_pos.y -
441 dia_font_ascent(message->text, message_font, MESSAGE_FONTHEIGHT);
442 rect.bottom = rect.top + MESSAGE_FONTHEIGHT;
443 rectangle_union(&obj->bounding_box, &rect);
447 static DiaObject *
448 message_load(ObjectNode obj_node, int version, const char *filename)
450 return object_load_using_properties(&message_type,
451 obj_node,version,filename);