UML comments bug #303744
[dia.git] / objects / UML / object.c
blobc9ad5277ccefb77f5a2521e3d41a9c5bf5959634
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>
26 #include <stdio.h>
28 #include "intl.h"
29 #include "object.h"
30 #include "element.h"
31 #include "diarenderer.h"
32 #include "attributes.h"
33 #include "text.h"
34 #include "properties.h"
36 #include "uml.h"
37 #include "stereotype.h"
39 #include "pixmaps/object.xpm"
41 typedef struct _Objet Objet;
43 #define NUM_CONNECTIONS 9
45 struct _Objet {
46 Element element;
48 ConnectionPoint connections[NUM_CONNECTIONS];
50 char *stereotype;
51 Text *text;
52 char *exstate; /* used for explicit state */
53 Text *attributes;
55 Color text_color;
56 Color line_color;
57 Color fill_color;
59 Point ex_pos, st_pos;
60 int is_active;
61 int show_attributes;
62 int is_multiple;
64 char *attrib;
66 char *st_stereotype;
69 #define OBJET_BORDERWIDTH 0.1
70 #define OBJET_ACTIVEBORDERWIDTH 0.2
71 #define OBJET_LINEWIDTH 0.05
72 #define OBJET_MARGIN_X 0.5
73 #define OBJET_MARGIN_Y 0.5
74 #define OBJET_MARGIN_M 0.4
75 #define OBJET_FONTHEIGHT 0.8
77 static real objet_distance_from(Objet *ob, Point *point);
78 static void objet_select(Objet *ob, Point *clicked_point,
79 DiaRenderer *interactive_renderer);
80 static ObjectChange* objet_move_handle(Objet *ob, Handle *handle,
81 Point *to, ConnectionPoint *cp,
82 HandleMoveReason reason, ModifierKeys modifiers);
83 static ObjectChange* objet_move(Objet *ob, Point *to);
84 static void objet_draw(Objet *ob, DiaRenderer *renderer);
85 static DiaObject *objet_create(Point *startpoint,
86 void *user_data,
87 Handle **handle1,
88 Handle **handle2);
89 static void objet_destroy(Objet *ob);
90 static DiaObject *objet_load(ObjectNode obj_node, int version,
91 const char *filename);
92 static PropDescription *objet_describe_props(Objet *objet);
93 static void objet_get_props(Objet *objet, GPtrArray *props);
94 static void objet_set_props(Objet *objet, GPtrArray *props);
95 static void objet_update_data(Objet *ob);
97 static ObjectTypeOps objet_type_ops =
99 (CreateFunc) objet_create,
100 (LoadFunc) objet_load,/*using_properties*/ /* load */
101 (SaveFunc) object_save_using_properties, /* save */
102 (GetDefaultsFunc) NULL,
103 (ApplyDefaultsFunc) NULL
106 /* Non-nice typo, needed for backwards compatibility. */
107 DiaObjectType objet_type =
109 "UML - Objet", /* name */
110 0, /* version */
111 (char **) object_xpm, /* pixmap */
113 &objet_type_ops /* ops */
116 DiaObjectType umlobject_type =
118 "UML - Object", /* name */
119 0, /* version */
120 (char **) object_xpm, /* pixmap */
122 &objet_type_ops /* ops */
125 static ObjectOps objet_ops = {
126 (DestroyFunc) objet_destroy,
127 (DrawFunc) objet_draw,
128 (DistanceFunc) objet_distance_from,
129 (SelectFunc) objet_select,
130 (CopyFunc) object_copy_using_properties,
131 (MoveFunc) objet_move,
132 (MoveHandleFunc) objet_move_handle,
133 (GetPropertiesFunc) object_create_props_dialog,
134 (ApplyPropertiesFunc) object_apply_props_from_dialog,
135 (ObjectMenuFunc) NULL,
136 (DescribePropsFunc) objet_describe_props,
137 (GetPropsFunc) objet_get_props,
138 (SetPropsFunc) objet_set_props,
142 static PropDescription objet_props[] = {
143 ELEMENT_COMMON_PROPERTIES,
144 PROP_STD_TEXT_COLOUR_OPTIONAL,
145 PROP_STD_LINE_COLOUR_OPTIONAL,
146 PROP_STD_FILL_COLOUR_OPTIONAL,
147 { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
148 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
149 N_("Stereotype"), NULL, NULL },
150 { "exstate", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
151 N_("Explicit state"),NULL, NULL },
152 { "attribstr", PROP_TYPE_MULTISTRING, PROP_FLAG_VISIBLE|PROP_FLAG_DONT_SAVE,
153 N_("Attributes"),NULL, GINT_TO_POINTER(6) },
154 { "attrib", PROP_TYPE_TEXT, 0, NULL,NULL, NULL },
155 { "is_active", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
156 N_("Active object"),NULL,NULL},
157 { "show_attribs", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
158 N_("Show attributes"),NULL,NULL},
159 { "multiple", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
160 N_("Multiple instance"),NULL,NULL},
161 PROP_DESC_END
164 static PropDescription *
165 objet_describe_props(Objet *ob)
167 if (objet_props[0].quark == 0) {
168 prop_desc_list_calculate_quarks(objet_props);
170 return objet_props;
173 static PropOffset objet_offsets[] = {
174 ELEMENT_COMMON_PROPERTIES_OFFSETS,
175 { "text_colour",PROP_TYPE_COLOUR,offsetof(Objet, text_color) },
176 { "line_colour",PROP_TYPE_COLOUR,offsetof(Objet, line_color) },
177 { "fill_colour",PROP_TYPE_COLOUR,offsetof(Objet, fill_color) },
178 { "name", PROP_TYPE_STRING, offsetof(Objet, exstate) },
179 { "stereotype", PROP_TYPE_STRING, offsetof(Objet, stereotype) },
180 { "text", PROP_TYPE_TEXT, offsetof(Objet, text) },
181 { "exstate", PROP_TYPE_STRING, offsetof(Objet, exstate) },
182 { "attribstr", PROP_TYPE_MULTISTRING, offsetof(Objet, attrib)},
183 { "attrib", PROP_TYPE_TEXT, offsetof(Objet, attributes)},
184 { "is_active", PROP_TYPE_BOOL, offsetof(Objet,is_active)},
185 { "show_attribs", PROP_TYPE_BOOL, offsetof(Objet, show_attributes)},
186 { "multiple", PROP_TYPE_BOOL, offsetof(Objet, is_multiple)},
187 { NULL, 0, 0 },
190 static void
191 objet_get_props(Objet * objet, GPtrArray *props)
193 if (objet->attrib) g_free(objet->attrib);
194 objet->attrib = text_get_string_copy(objet->attributes);
196 object_get_props_from_offsets(&objet->element.object,
197 objet_offsets,props);
200 static void
201 objet_set_props(Objet *objet, GPtrArray *props)
203 object_set_props_from_offsets(&objet->element.object,
204 objet_offsets,props);
205 apply_textstr_properties(props,objet->attributes,"attrib",objet->attrib);
206 g_free(objet->st_stereotype);
207 objet->st_stereotype = NULL;
208 objet_update_data(objet);
211 static real
212 objet_distance_from(Objet *ob, Point *point)
214 DiaObject *obj = &ob->element.object;
215 return distance_rectangle_point(&obj->bounding_box, point);
218 static void
219 objet_select(Objet *ob, Point *clicked_point,
220 DiaRenderer *interactive_renderer)
222 text_set_cursor(ob->text, clicked_point, interactive_renderer);
223 text_grab_focus(ob->text, &ob->element.object);
224 element_update_handles(&ob->element);
227 static ObjectChange*
228 objet_move_handle(Objet *ob, Handle *handle,
229 Point *to, ConnectionPoint *cp,
230 HandleMoveReason reason, ModifierKeys modifiers)
232 assert(ob!=NULL);
233 assert(handle!=NULL);
234 assert(to!=NULL);
236 assert(handle->id < 8);
238 return NULL;
241 static ObjectChange*
242 objet_move(Objet *ob, Point *to)
244 ob->element.corner = *to;
245 objet_update_data(ob);
247 return NULL;
250 static void
251 objet_draw(Objet *ob, DiaRenderer *renderer)
253 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
254 Element *elem;
255 real bw, x, y, w, h;
256 Point p1, p2;
257 int i;
259 assert(ob != NULL);
260 assert(renderer != NULL);
262 elem = &ob->element;
264 x = elem->corner.x;
265 y = elem->corner.y;
266 w = elem->width;
267 h = elem->height;
269 bw = (ob->is_active) ? OBJET_ACTIVEBORDERWIDTH: OBJET_BORDERWIDTH;
271 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
272 renderer_ops->set_linewidth(renderer, bw);
273 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
276 p1.x = x; p1.y = y;
277 p2.x = x+w; p2.y = y+h;
279 if (ob->is_multiple) {
280 p1.x += OBJET_MARGIN_M;
281 p2.y -= OBJET_MARGIN_M;
282 renderer_ops->fill_rect(renderer,
283 &p1, &p2,
284 &ob->fill_color);
285 renderer_ops->draw_rect(renderer,
286 &p1, &p2,
287 &ob->line_color);
288 p1.x -= OBJET_MARGIN_M;
289 p1.y += OBJET_MARGIN_M;
290 p2.x -= OBJET_MARGIN_M;
291 p2.y += OBJET_MARGIN_M;
292 y += OBJET_MARGIN_M;
295 renderer_ops->fill_rect(renderer,
296 &p1, &p2,
297 &ob->fill_color);
298 renderer_ops->draw_rect(renderer,
299 &p1, &p2,
300 &ob->line_color);
303 text_draw(ob->text, renderer);
305 if ((ob->st_stereotype != NULL) && (ob->st_stereotype[0] != '\0')) {
306 renderer_ops->draw_string(renderer,
307 ob->st_stereotype,
308 &ob->st_pos, ALIGN_CENTER,
309 &ob->text_color);
312 if ((ob->exstate != NULL) && (ob->exstate[0] != '\0')) {
313 renderer_ops->draw_string(renderer,
314 ob->exstate,
315 &ob->ex_pos, ALIGN_CENTER,
316 &ob->text_color);
319 /* Is there a better way to underline? */
320 p1.x = x + (w - ob->text->max_width)/2;
321 p1.y = ob->text->position.y + ob->text->descent;
322 p2.x = p1.x + ob->text->max_width;
323 p2.y = p1.y;
325 renderer_ops->set_linewidth(renderer, OBJET_LINEWIDTH);
327 for (i=0; i<ob->text->numlines; i++) {
328 p1.x = x + (w - ob->text->row_width[i])/2;
329 p2.x = p1.x + ob->text->row_width[i];
330 renderer_ops->draw_line(renderer,
331 &p1, &p2,
332 &ob->text_color);
333 p1.y = p2.y += ob->text->height;
336 if (ob->show_attributes) {
337 p1.x = x; p2.x = x + w;
338 p1.y = p2.y = ob->attributes->position.y - ob->attributes->ascent - OBJET_MARGIN_Y;
340 renderer_ops->set_linewidth(renderer, bw);
341 renderer_ops->draw_line(renderer,
342 &p1, &p2,
343 &ob->line_color);
345 text_draw(ob->attributes, renderer);
349 static void
350 objet_update_data(Objet *ob)
352 Element *elem = &ob->element;
353 DiaObject *obj = &elem->object;
354 DiaFont *font;
355 Point p1, p2;
356 real h, w = 0;
358 text_calc_boundingbox(ob->text, NULL);
359 ob->stereotype = remove_stereotype_from_string(ob->stereotype);
360 if (!ob->st_stereotype) {
361 ob->st_stereotype = string_to_stereotype(ob->stereotype);
364 font = ob->text->font;
365 h = elem->corner.y + OBJET_MARGIN_Y;
367 if (ob->is_multiple) {
368 h += OBJET_MARGIN_M;
371 if ((ob->stereotype != NULL) && (ob->stereotype[0] != '\0')) {
372 w = dia_font_string_width(ob->st_stereotype, font, OBJET_FONTHEIGHT);
373 h += OBJET_FONTHEIGHT;
374 ob->st_pos.y = h;
375 h += OBJET_MARGIN_Y/2.0;
378 w = MAX(w, ob->text->max_width);
379 p1.y = h + ob->text->ascent; /* position of text */
381 h += ob->text->height*ob->text->numlines;
383 if ((ob->exstate != NULL) && (ob->exstate[0] != '\0')) {
384 w = MAX(w, dia_font_string_width(ob->exstate, font, OBJET_FONTHEIGHT));
385 h += OBJET_FONTHEIGHT;
386 ob->ex_pos.y = h;
389 h += OBJET_MARGIN_Y;
391 if (ob->show_attributes) {
392 h += OBJET_MARGIN_Y + ob->attributes->ascent;
393 p2.x = elem->corner.x + OBJET_MARGIN_X;
394 p2.y = h;
395 text_set_position(ob->attributes, &p2);
397 h += ob->attributes->height*ob->attributes->numlines;
399 w = MAX(w, ob->attributes->max_width);
402 w += 2*OBJET_MARGIN_X;
404 p1.x = elem->corner.x + w/2.0;
405 text_set_position(ob->text, &p1);
407 ob->ex_pos.x = ob->st_pos.x = p1.x;
410 if (ob->is_multiple) {
411 w += OBJET_MARGIN_M;
414 elem->width = w;
415 elem->height = h - elem->corner.y;
417 /* Update connections: */
418 element_update_connections_rectangle(elem, ob->connections);
420 element_update_boundingbox(elem);
421 obj->position = elem->corner;
422 element_update_handles(elem);
425 static DiaObject *
426 objet_create(Point *startpoint,
427 void *user_data,
428 Handle **handle1,
429 Handle **handle2)
431 Objet *ob;
432 Element *elem;
433 DiaObject *obj;
434 Point p;
435 DiaFont *font;
436 int i;
438 ob = g_malloc0(sizeof(Objet));
439 elem = &ob->element;
440 obj = &elem->object;
442 obj->type = &umlobject_type;
444 obj->ops = &objet_ops;
446 elem->corner = *startpoint;
448 ob->text_color = color_black;
449 ob->line_color = attributes_get_foreground();
450 ob->fill_color = attributes_get_background();
452 font = dia_font_new_from_style(DIA_FONT_SANS, OBJET_FONTHEIGHT);
454 ob->show_attributes = FALSE;
455 ob->is_active = FALSE;
456 ob->is_multiple = FALSE;
458 ob->exstate = NULL;
459 ob->stereotype = NULL;
460 ob->st_stereotype = NULL;
462 /* The text position is recalculated later */
463 p.x = 0.0;
464 p.y = 0.0;
465 ob->attributes = new_text("", font, 0.8, &p, &color_black, ALIGN_LEFT);
466 ob->attrib = NULL;
467 ob->text = new_text("", font, 0.8, &p, &color_black, ALIGN_CENTER);
469 dia_font_unref(font);
471 element_init(elem, 8, NUM_CONNECTIONS);
473 for (i=0;i<NUM_CONNECTIONS;i++) {
474 obj->connections[i] = &ob->connections[i];
475 ob->connections[i].object = obj;
476 ob->connections[i].connected = NULL;
478 ob->connections[8].flags = CP_FLAGS_MAIN;
479 elem->extra_spacing.border_trans = OBJET_BORDERWIDTH/2.0;
480 objet_update_data(ob);
482 for (i=0;i<8;i++) {
483 obj->handles[i]->type = HANDLE_NON_MOVABLE;
486 *handle1 = NULL;
487 *handle2 = NULL;
489 return &ob->element.object;
492 static void
493 objet_destroy(Objet *ob)
495 text_destroy(ob->text);
496 text_destroy(ob->attributes);
498 g_free(ob->stereotype);
499 g_free(ob->st_stereotype);
500 g_free(ob->exstate);
501 g_free(ob->attrib);
503 element_destroy(&ob->element);
506 static DiaObject *
507 objet_load(ObjectNode obj_node, int version, const char *filename)
509 return object_load_using_properties(&objet_type,
510 obj_node,version,filename);