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.
32 #include "connection.h"
33 #include "diarenderer.h"
36 #include "properties.h"
37 #include "attributes.h"
39 #include "pixmaps/message.xpm"
43 typedef struct _Message Message
;
51 MESSAGE_SEND
, /* Asynchronous */
56 Connection connection
;
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
,
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 */
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
},
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
},
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
) },
179 message_get_props(Message
* message
, GPtrArray
*props
)
181 object_get_props_from_offsets(&message
->connection
.object
,
182 message_offsets
, props
);
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
);
195 message_distance_from(Message
*message
, Point
*point
)
200 endpoints
= &message
->connection
.endpoints
[0];
202 dist
= distance_line_point(&endpoints
[0], &endpoints
[1], MESSAGE_WIDTH
, point
);
208 message_select(Message
*message
, Point
*clicked_point
,
209 DiaRenderer
*interactive_renderer
)
211 connection_update_handles(&message
->connection
);
215 message_move_handle(Message
*message
, Handle
*handle
,
216 Point
*to
, ConnectionPoint
*cp
,
217 HandleMoveReason reason
, ModifierKeys modifiers
)
222 assert(message
!=NULL
);
223 assert(handle
!=NULL
);
226 if (handle
->id
== HANDLE_MOVE_TEXT
) {
227 message
->text_pos
= *to
;
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
);
236 point_add(&message
->text_pos
, &p2
);
239 message_update_data(message
);
245 message_move(Message
*message
, Point
*to
)
248 Point
*endpoints
= &message
->connection
.endpoints
[0];
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
);
268 message_draw(Message
*message
, DiaRenderer
*renderer
)
270 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
271 Point
*endpoints
, p1
, p2
, px
;
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
;
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
) {
299 if (message
->type
==MESSAGE_RETURN
) {
300 renderer_ops
->set_dashlength(renderer
, MESSAGE_DASHLEN
);
301 renderer_ops
->set_linestyle(renderer
, LINESTYLE_DASHED
);
305 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
310 if (message
->type
==MESSAGE_RECURSIVE
) {
313 renderer_ops
->draw_line(renderer
,
315 &message
->line_color
);
317 renderer_ops
->draw_line(renderer
,
319 &message
->line_color
);
323 renderer_ops
->draw_line_with_arrows(renderer
,
326 &message
->line_color
,
329 renderer_ops
->set_font(renderer
, message_font
,
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
);
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
)
353 message_create(Point
*startpoint
,
363 if (message_font
== NULL
) {
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;
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
;
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
;
409 message_destroy(Message
*message
)
411 connection_destroy(&message
->connection
);
413 g_free(message
->text
);
417 message_update_data(Message
*message
)
419 Connection
*conn
= &message
->connection
;
420 DiaObject
*obj
= &conn
->object
;
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
);
448 message_load(ObjectNode obj_node
, int version
, const char *filename
)
450 return object_load_using_properties(&message_type
,
451 obj_node
,version
,filename
);