2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / UML / usecase.c
blob2f17c0c8301c416dcb36a82f728f4e1e34a94821
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 "element.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
32 #include "text.h"
33 #include "properties.h"
35 #include "pixmaps/case.xpm"
37 typedef struct _UsecasePropertiesDialog UsecasePropertiesDialog;
38 typedef struct _Usecase Usecase;
39 typedef struct _UsecaseState UsecaseState;
41 #define NUM_CONNECTIONS 9
43 struct _Usecase {
44 Element element;
46 ConnectionPoint connections[NUM_CONNECTIONS];
48 Text *text;
49 int text_outside;
50 int collaboration;
51 TextAttributes attrs;
53 Color line_color;
54 Color fill_color;
58 struct _UsecasePropertiesDialog {
59 GtkWidget *dialog;
61 GtkToggleButton *text_out;
62 GtkToggleButton *collaboration;
66 #define USECASE_WIDTH 3.25
67 #define USECASE_HEIGHT 2
68 #define USECASE_MIN_RATIO 1.5
69 #define USECASE_MAX_RATIO 3
70 #define USECASE_LINEWIDTH 0.1
71 #define USECASE_MARGIN_Y 0.3
73 static real usecase_distance_from(Usecase *usecase, Point *point);
74 static void usecase_select(Usecase *usecase, Point *clicked_point,
75 DiaRenderer *interactive_renderer);
76 static ObjectChange* usecase_move_handle(Usecase *usecase, Handle *handle,
77 Point *to, ConnectionPoint *cp,
78 HandleMoveReason reason, ModifierKeys modifiers);
79 static ObjectChange* usecase_move(Usecase *usecase, Point *to);
80 static void usecase_draw(Usecase *usecase, DiaRenderer *renderer);
81 static DiaObject *usecase_create(Point *startpoint,
82 void *user_data,
83 Handle **handle1,
84 Handle **handle2);
85 static void usecase_destroy(Usecase *usecase);
86 static DiaObject *usecase_load(ObjectNode obj_node, int version,
87 const char *filename);
88 static void usecase_update_data(Usecase *usecase);
89 static PropDescription *usecase_describe_props(Usecase *usecase);
90 static void usecase_get_props(Usecase *usecase, GPtrArray *props);
91 static void usecase_set_props(Usecase *usecase, GPtrArray *props);
93 static ObjectTypeOps usecase_type_ops =
95 (CreateFunc) usecase_create,
96 (LoadFunc) usecase_load,/*using_properties*/ /* load */
97 (SaveFunc) object_save_using_properties, /* save */
98 (GetDefaultsFunc) NULL,
99 (ApplyDefaultsFunc) NULL
102 DiaObjectType usecase_type =
104 "UML - Usecase", /* name */
105 0, /* version */
106 (char **) case_xpm, /* pixmap */
108 &usecase_type_ops /* ops */
111 static ObjectOps usecase_ops = {
112 (DestroyFunc) usecase_destroy,
113 (DrawFunc) usecase_draw,
114 (DistanceFunc) usecase_distance_from,
115 (SelectFunc) usecase_select,
116 (CopyFunc) object_copy_using_properties,
117 (MoveFunc) usecase_move,
118 (MoveHandleFunc) usecase_move_handle,
119 (GetPropertiesFunc) object_create_props_dialog,
120 (ApplyPropertiesFunc) object_apply_props_from_dialog,
121 (ObjectMenuFunc) NULL,
122 (DescribePropsFunc) usecase_describe_props,
123 (GetPropsFunc) usecase_get_props,
124 (SetPropsFunc) usecase_set_props
127 static PropDescription usecase_props[] = {
128 ELEMENT_COMMON_PROPERTIES,
129 PROP_STD_LINE_COLOUR_OPTIONAL,
130 PROP_STD_FILL_COLOUR_OPTIONAL,
131 { "text_outside", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
132 N_("Text outside"), NULL, NULL },
133 { "collaboration", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
134 N_("Collaboration"), NULL, NULL },
135 PROP_STD_TEXT_FONT,
136 PROP_STD_TEXT_HEIGHT,
137 PROP_STD_TEXT_COLOUR_OPTIONAL,
138 { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
140 PROP_DESC_END
143 static PropDescription *
144 usecase_describe_props(Usecase *usecase)
146 if (usecase_props[0].quark == 0) {
147 prop_desc_list_calculate_quarks(usecase_props);
149 return usecase_props;
152 static PropOffset usecase_offsets[] = {
153 ELEMENT_COMMON_PROPERTIES_OFFSETS,
154 {"line_colour", PROP_TYPE_COLOUR, offsetof(Usecase, line_color) },
155 {"fill_colour", PROP_TYPE_COLOUR, offsetof(Usecase, fill_color) },
156 {"text_outside", PROP_TYPE_BOOL, offsetof(Usecase, text_outside) },
157 {"collaboration", PROP_TYPE_BOOL, offsetof(Usecase, collaboration) },
158 {"text",PROP_TYPE_TEXT,offsetof(Usecase,text)},
159 {"text_font",PROP_TYPE_FONT,offsetof(Usecase,attrs.font)},
160 {"text_height",PROP_TYPE_REAL,offsetof(Usecase,attrs.height)},
161 {"text_colour",PROP_TYPE_COLOUR,offsetof(Usecase,attrs.color)},
162 { NULL, 0, 0 },
165 static void
166 usecase_get_props(Usecase * usecase, GPtrArray *props)
168 text_get_attributes(usecase->text,&usecase->attrs);
169 object_get_props_from_offsets(&usecase->element.object,
170 usecase_offsets,props);
173 static void
174 usecase_set_props(Usecase *usecase, GPtrArray *props)
176 object_set_props_from_offsets(&usecase->element.object,
177 usecase_offsets,props);
178 apply_textattr_properties(props,usecase->text,"text",&usecase->attrs);
179 usecase_update_data(usecase);
182 static real
183 usecase_distance_from(Usecase *usecase, Point *point)
185 DiaObject *obj = &usecase->element.object;
186 return distance_rectangle_point(&obj->bounding_box, point);
189 static void
190 usecase_select(Usecase *usecase, Point *clicked_point,
191 DiaRenderer *interactive_renderer)
193 text_set_cursor(usecase->text, clicked_point, interactive_renderer);
194 text_grab_focus(usecase->text, &usecase->element.object);
195 element_update_handles(&usecase->element);
198 static ObjectChange*
199 usecase_move_handle(Usecase *usecase, Handle *handle,
200 Point *to, ConnectionPoint *cp,
201 HandleMoveReason reason, ModifierKeys modifiers)
203 assert(usecase!=NULL);
204 assert(handle!=NULL);
205 assert(to!=NULL);
207 assert(handle->id < 8);
209 return NULL;
212 static ObjectChange*
213 usecase_move(Usecase *usecase, Point *to)
215 real h;
216 Point p;
218 usecase->element.corner = *to;
219 h = usecase->text->height*usecase->text->numlines;
221 p = *to;
222 p.x += usecase->element.width/2.0;
223 if (usecase->text_outside) {
224 p.y += usecase->element.height - h + usecase->text->ascent;
225 } else {
226 p.y += (usecase->element.height - h)/2.0 + usecase->text->ascent;
228 text_set_position(usecase->text, &p);
229 usecase_update_data(usecase);
231 return NULL;
234 static void
235 usecase_draw(Usecase *usecase, DiaRenderer *renderer)
237 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
238 Element *elem;
239 real x, y, w, h;
240 Point c;
242 assert(usecase != NULL);
243 assert(renderer != NULL);
245 elem = &usecase->element;
247 x = elem->corner.x;
248 y = elem->corner.y;
250 if (usecase->text_outside) {
251 w = USECASE_WIDTH;
252 h = USECASE_HEIGHT;
253 c.x = x + elem->width/2.0;
254 c.y = y + USECASE_HEIGHT / 2.0;
255 } else {
256 w = elem->width;
257 h = elem->height;
258 c.x = x + w/2.0;
259 c.y = y + h/2.0;
263 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
264 renderer_ops->set_linewidth(renderer, USECASE_LINEWIDTH);
266 if (usecase->collaboration)
267 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
268 else
269 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
271 renderer_ops->fill_ellipse(renderer,
273 w, h,
274 &usecase->fill_color);
275 renderer_ops->draw_ellipse(renderer,
277 w, h,
278 &usecase->line_color);
280 text_draw(usecase->text, renderer);
284 static void
285 usecase_update_data(Usecase *usecase)
287 real w, h, ratio;
288 Point c, half, r,p;
290 Element *elem = &usecase->element;
291 DiaObject *obj = &elem->object;
293 text_calc_boundingbox(usecase->text, NULL);
294 w = usecase->text->max_width;
295 h = usecase->text->height*usecase->text->numlines;
297 if (!usecase->text_outside) {
298 ratio = w/h;
300 if (ratio > USECASE_MAX_RATIO)
301 ratio = USECASE_MAX_RATIO;
303 if (ratio < USECASE_MIN_RATIO) {
304 ratio = USECASE_MIN_RATIO;
305 r.y = w / ratio + h;
306 r.x = r.y * ratio;
307 } else {
308 r.x = ratio*h + w;
309 r.y = r.x / ratio;
311 if (r.x < USECASE_WIDTH)
312 r.x = USECASE_WIDTH;
313 if (r.y < USECASE_HEIGHT)
314 r.y = USECASE_HEIGHT;
315 } else {
316 r.x = USECASE_WIDTH;
317 r.y = USECASE_HEIGHT;
320 elem->width = r.x;
321 elem->height = r.y;
323 if (usecase->text_outside) {
324 elem->width = MAX(elem->width, w);
325 elem->height += h + USECASE_MARGIN_Y;
328 r.x /= 2.0;
329 r.y /= 2.0;
330 c.x = elem->corner.x + elem->width / 2.0;
331 c.y = elem->corner.y + r.y;
332 half.x = r.x * M_SQRT1_2;
333 half.y = r.y * M_SQRT1_2;
335 /* Update connections: */
336 usecase->connections[0].pos.x = c.x - half.x;
337 usecase->connections[0].pos.y = c.y - half.y;
338 usecase->connections[1].pos.x = c.x;
339 usecase->connections[1].pos.y = elem->corner.y;
340 usecase->connections[2].pos.x = c.x + half.x;
341 usecase->connections[2].pos.y = c.y - half.y;
342 usecase->connections[3].pos.x = c.x - r.x;
343 usecase->connections[3].pos.y = c.y;
344 usecase->connections[4].pos.x = c.x + r.x;
345 usecase->connections[4].pos.y = c.y;
347 if (usecase->text_outside) {
348 usecase->connections[5].pos.x = elem->corner.x;
349 usecase->connections[5].pos.y = elem->corner.y + elem->height;
350 usecase->connections[6].pos.x = c.x;
351 usecase->connections[6].pos.y = elem->corner.y + elem->height;
352 usecase->connections[7].pos.x = elem->corner.x + elem->width;
353 usecase->connections[7].pos.y = elem->corner.y + elem->height;
354 } else {
355 usecase->connections[5].pos.x = c.x - half.x;
356 usecase->connections[5].pos.y = c.y + half.y;
357 usecase->connections[6].pos.x = c.x;
358 usecase->connections[6].pos.y = elem->corner.y + elem->height;
359 usecase->connections[7].pos.x = c.x + half.x;
360 usecase->connections[7].pos.y = c.y + half.y;
362 usecase->connections[8].pos.x = c.x;
363 usecase->connections[8].pos.y = c.y;
365 usecase->connections[0].directions = DIR_NORTH|DIR_WEST;
366 usecase->connections[1].directions = DIR_NORTH;
367 usecase->connections[2].directions = DIR_NORTH|DIR_EAST;
368 usecase->connections[3].directions = DIR_WEST;
369 usecase->connections[4].directions = DIR_EAST;
370 usecase->connections[5].directions = DIR_SOUTH|DIR_WEST;
371 usecase->connections[6].directions = DIR_SOUTH;
372 usecase->connections[7].directions = DIR_SOUTH|DIR_EAST;
373 usecase->connections[8].directions = DIR_ALL;
375 h = usecase->text->height*usecase->text->numlines;
376 p = usecase->element.corner;
377 p.x += usecase->element.width/2.0;
378 if (usecase->text_outside) {
379 p.y += usecase->element.height - h + usecase->text->ascent;
380 } else {
381 p.y += (usecase->element.height - h)/2.0 + usecase->text->ascent;
383 text_set_position(usecase->text, &p);
385 element_update_boundingbox(elem);
387 obj->position = elem->corner;
389 element_update_handles(elem);
393 static DiaObject *
394 usecase_create(Point *startpoint,
395 void *user_data,
396 Handle **handle1,
397 Handle **handle2)
399 Usecase *usecase;
400 Element *elem;
401 DiaObject *obj;
402 Point p;
403 DiaFont *font;
404 int i;
406 usecase = g_malloc0(sizeof(Usecase));
407 elem = &usecase->element;
408 obj = &elem->object;
410 obj->type = &usecase_type;
411 obj->ops = &usecase_ops;
412 elem->corner = *startpoint;
413 elem->width = USECASE_WIDTH;
414 elem->height = USECASE_HEIGHT;
416 usecase->line_color = attributes_get_foreground();
417 usecase->fill_color = attributes_get_background();
419 font = dia_font_new_from_style(DIA_FONT_SANS, 0.8);
420 p = *startpoint;
421 p.x += USECASE_WIDTH/2.0;
422 p.y += USECASE_HEIGHT/2.0;
424 usecase->text = new_text("", font, 0.8, &p, &color_black, ALIGN_CENTER);
425 dia_font_unref(font);
426 text_get_attributes(usecase->text,&usecase->attrs);
427 usecase->text_outside = 0;
428 usecase->collaboration = 0;
429 element_init(elem, 8, NUM_CONNECTIONS);
431 for (i=0;i<NUM_CONNECTIONS;i++) {
432 obj->connections[i] = &usecase->connections[i];
433 usecase->connections[i].object = obj;
434 usecase->connections[i].connected = NULL;
436 usecase->connections[8].flags = CP_FLAGS_MAIN;
437 elem->extra_spacing.border_trans = 0.0;
438 usecase_update_data(usecase);
440 for (i=0;i<8;i++) {
441 obj->handles[i]->type = HANDLE_NON_MOVABLE;
444 *handle1 = NULL;
445 *handle2 = NULL;
446 return &usecase->element.object;
449 static void
450 usecase_destroy(Usecase *usecase)
452 text_destroy(usecase->text);
454 element_destroy(&usecase->element);
457 static DiaObject *
458 usecase_load(ObjectNode obj_node, int version, const char *filename)
460 return object_load_using_properties(&usecase_type,
461 obj_node,version,filename);