1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
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.
8 *
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.
13 *
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.
17 *
18 * File: class.c
19 *
20 * Purpose: This file contains implementation of the "class" code.
21 */
22
23 /** \file objects/UML/class.c Implementation of the 'UML - Class' type */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <assert.h>
30 #include <gtk/gtk.h>
31 #include <math.h>
32 #include <string.h>
33
34 #include "intl.h"
35 #include "diarenderer.h"
36 #include "attributes.h"
37 #include "properties.h"
38 #include "diamenu.h"
39
40 #include "class.h"
41
42 #include "pixmaps/umlclass.xpm"
43
44 #include "debug.h"
45
46 #define UMLCLASS_BORDER 0.1
47 #define UMLCLASS_UNDERLINEWIDTH 0.05
48 #define UMLCLASS_TEMPLATE_OVERLAY_X 2.3
49 #define UMLCLASS_TEMPLATE_OVERLAY_Y 0.3
50
51 static real umlclass_distance_from(UMLClass *umlclass, Point *point);
52 static void umlclass_select(UMLClass *umlclass, Point *clicked_point,
53 DiaRenderer *interactive_renderer);
54 static ObjectChange* umlclass_move_handle(UMLClass *umlclass, Handle *handle,
55 Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers);
56 static ObjectChange* umlclass_move(UMLClass *umlclass, Point *to);
57 static void umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer);
58 static DiaObject *umlclass_create(Point *startpoint,
59 void *user_data,
60 Handle **handle1,
61 Handle **handle2);
62 static void umlclass_destroy(UMLClass *umlclass);
63 static DiaObject *umlclass_copy(UMLClass *umlclass);
64
65 static void umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
66 const char *filename);
67 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
68 const char *filename);
69
70 static DiaMenu * umlclass_object_menu(DiaObject *obj, Point *p);
71 static ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data);
72
73 static PropDescription *umlclass_describe_props(UMLClass *umlclass);
74 static void umlclass_get_props(UMLClass *umlclass, GPtrArray *props);
75 static void umlclass_set_props(UMLClass *umlclass, GPtrArray *props);
76
77 static void fill_in_fontdata(UMLClass *umlclass);
78 static int umlclass_num_dynamic_connectionpoints(UMLClass *class);
79
80 static ObjectChange *_umlclass_apply_props_from_dialog(UMLClass *umlclass, GtkWidget *widget);
81
82 static ObjectTypeOps umlclass_type_ops =
83 {
84 (CreateFunc) umlclass_create,
85 (LoadFunc) umlclass_load,
86 (SaveFunc) umlclass_save
87 };
88
89 /**
90 * This is the type descriptor for a UML - Class. It contains the
91 * information used by Dia to create an object of this type. The structure
92 * of this data type is defined in the header file object.h. When a
93 * derivation of class is required, then this type can be copied and then
94 * change the name and any other fields that are variances from the base
95 * type.
96 */
97 DiaObjectType umlclass_type =
98 {
99 "UML - Class", /* name */
100 0, /* version */
101 (char **) umlclass_xpm, /* pixmap */
102
103 ¨class_type_ops, /* ops */
104 NULL,
105 (void*)0
106 };
107
108 /** \brief vtable for UMLClass */
109 static ObjectOps umlclass_ops = {
110 (DestroyFunc) umlclass_destroy,
111 (DrawFunc) umlclass_draw,
112 (DistanceFunc) umlclass_distance_from,
113 (SelectFunc) umlclass_select,
114 (CopyFunc) umlclass_copy,
115 (MoveFunc) umlclass_move,
116 (MoveHandleFunc) umlclass_move_handle,
117 (GetPropertiesFunc) umlclass_get_properties,
118 (ApplyPropertiesDialogFunc) _umlclass_apply_props_from_dialog,
119 (ObjectMenuFunc) umlclass_object_menu,
120 (DescribePropsFunc) umlclass_describe_props,
121 (GetPropsFunc) umlclass_get_props,
122 (SetPropsFunc) umlclass_set_props,
123 (TextEditFunc) 0,
124 (ApplyPropertiesListFunc) object_apply_props,
125 };
126
127 extern PropDescDArrayExtra umlattribute_extra;
128 extern PropDescDArrayExtra umloperation_extra;
129 extern PropDescDArrayExtra umlparameter_extra;
130 extern PropDescDArrayExtra umlformalparameter_extra;
131
132 /** Properties of UMLClass */
133 static PropDescription umlclass_props[] = {
134 ELEMENT_COMMON_PROPERTIES,
135 PROP_STD_LINE_WIDTH_OPTIONAL,
136 /* 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 */
137 PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
138 PROP_STD_LINE_COLOUR_OPTIONAL,
139 PROP_STD_FILL_COLOUR_OPTIONAL,
140
141 PROP_STD_NOTEBOOK_BEGIN,
142 PROP_NOTEBOOK_PAGE("class", PROP_FLAG_DONT_MERGE, N_("Class")),
143 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_NO_DEFAULTS,
144 N_("Name"), NULL, NULL },
145 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
146 N_("Stereotype"), NULL, NULL },
147 { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
148 N_("Comment"), NULL, NULL },
149 { "abstract", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
150 N_("Abstract"), NULL, NULL },
151 { "template", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_NO_DEFAULTS,
152 N_("Template"), NULL, NULL },
153
154 { "suppress_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
155 N_("Suppress Attributes"), NULL, NULL },
156 { "suppress_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
157 N_("Suppress Operations"), NULL, NULL },
158 { "visible_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
159 N_("Visible Attributes"), NULL, NULL },
160 { "visible_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
161 N_("Visible Operations"), NULL, NULL },
162 { "visible_comments", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
163 N_("Visible Comments"), NULL, NULL },
164 { "wrap_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
165 N_("Wrap Operations"), NULL, NULL },
166 { "wrap_after_char", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
167 N_("Wrap after char"), NULL, NULL },
168 { "comment_line_length", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
169 N_("Comment line length"), NULL, NULL},
170 { "comment_tagging", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
171 N_("Comment tagging"), NULL, NULL},
172
173 /* all this just to make the defaults selectable ... */
174 PROP_NOTEBOOK_PAGE("font", PROP_FLAG_DONT_MERGE, N_("Font")),
175 PROP_MULTICOL_BEGIN("class"),
176 PROP_MULTICOL_COLUMN("font"),
177 /* FIXME: apparently multicol does not work correctly, this should be FIRST column */
178 { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
179 N_("Normal"), NULL, NULL },
180 { "polymorphic_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
181 N_("Polymorphic"), NULL, NULL },
182 { "abstract_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
183 N_("Abstract"), NULL, NULL },
184 { "classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
185 N_("Classname"), NULL, NULL },
186 { "abstract_classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
187 N_("Abstract Classname"), NULL, NULL },
188 { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
189 N_("Comment"), NULL, NULL },
190
191 PROP_MULTICOL_COLUMN("height"),
192 { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
193 N_(" "), NULL, NULL },
194 { "polymorphic_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
195 N_(" "), NULL, NULL },
196 { "abstract_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
197 N_(" "), NULL, NULL },
198 { "classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
199 N_(" "), NULL, NULL },
200 { "abstract_classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
201 N_(" "), NULL, NULL },
202 { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
203 N_(" "), NULL, NULL },
204 PROP_MULTICOL_END("class"),
205 PROP_STD_NOTEBOOK_END,
206
207 /* these are used during load, but currently not during save */
208 { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
209 N_("Attributes"), NULL, NULL /* umlattribute_extra */ },
210 { "operations", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
211 N_("Operations"), NULL, NULL /* umloperations_extra */ },
212 /* the naming is questionable, but kept for compatibility */
213 { "templates", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
214 N_("Template Parameters"), NULL, NULL /* umlformalparameters_extra */ },
215
216 PROP_DESC_END
217 };
218
219 ObjectChange *
_umlclass_apply_props_from_dialog(UMLClass * umlclass,GtkWidget * widget)220 _umlclass_apply_props_from_dialog(UMLClass *umlclass, GtkWidget *widget)
221 {
222 DiaObject *obj = ¨class->element.object;
223 /* fallback, if it isn't our dialog, e.g. during multiple selection change */
224 if (!umlclass->properties_dialog)
225 return object_apply_props_from_dialog (obj, widget);
226 else
227 return umlclass_apply_props_from_dialog (umlclass, widget);
228 }
229
230 static PropDescription *
umlclass_describe_props(UMLClass * umlclass)231 umlclass_describe_props(UMLClass *umlclass)
232 {
233 if (umlclass_props[0].quark == 0) {
234 int i = 0;
235
236 prop_desc_list_calculate_quarks(umlclass_props);
237 while (umlclass_props[i].name != NULL) {
238 /* can't do this static, at least not on win32
239 * due to relocation (initializer not a constant)
240 */
241 if (0 == strcmp(umlclass_props[i].name, "attributes"))
242 umlclass_props[i].extra_data = ¨attribute_extra;
243 else if (0 == strcmp(umlclass_props[i].name, "operations")) {
244 PropDescription *records = umloperation_extra.common.record;
245 int j = 0;
246
247 umlclass_props[i].extra_data = ¨operation_extra;
248 while (records[j].name != NULL) {
249 if (0 == strcmp(records[j].name, "parameters"))
250 records[j].extra_data = ¨parameter_extra;
251 j++;
252 }
253 }
254 else if (0 == strcmp(umlclass_props[i].name, "templates"))
255 umlclass_props[i].extra_data = ¨formalparameter_extra;
256
257 i++;
258 }
259 }
260 return umlclass_props;
261 }
262
263 static PropOffset umlclass_offsets[] = {
264 ELEMENT_COMMON_PROPERTIES_OFFSETS,
265
266 { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(UMLClass, line_width) },
267 { "text_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, text_color) },
268 { "line_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, line_color) },
269 { "fill_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, fill_color) },
270 { "name", PROP_TYPE_STRING, offsetof(UMLClass, name) },
271 { "stereotype", PROP_TYPE_STRING, offsetof(UMLClass, stereotype) },
272 { "comment", PROP_TYPE_STRING, offsetof(UMLClass, comment) },
273 { "abstract", PROP_TYPE_BOOL, offsetof(UMLClass, abstract) },
274 { "template", PROP_TYPE_BOOL, offsetof(UMLClass, template) },
275 { "suppress_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_attributes) },
276 { "visible_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , visible_attributes) },
277 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
278 { "suppress_operations", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_operations) },
279 { "visible_operations", PROP_TYPE_BOOL, offsetof(UMLClass , visible_operations) },
280 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
281 { "wrap_operations", PROP_TYPE_BOOL, offsetof(UMLClass , wrap_operations) },
282 { "wrap_after_char", PROP_TYPE_INT, offsetof(UMLClass , wrap_after_char) },
283 { "comment_line_length", PROP_TYPE_INT, offsetof(UMLClass, comment_line_length) },
284 { "comment_tagging", PROP_TYPE_BOOL, offsetof(UMLClass, comment_tagging) },
285
286 /* all this just to make the defaults selectable ... */
287 PROP_OFFSET_MULTICOL_BEGIN("class"),
288 PROP_OFFSET_MULTICOL_COLUMN("font"),
289 { "normal_font", PROP_TYPE_FONT, offsetof(UMLClass, normal_font) },
290 { "abstract_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_font) },
291 { "polymorphic_font", PROP_TYPE_FONT, offsetof(UMLClass, polymorphic_font) },
292 { "classname_font", PROP_TYPE_FONT, offsetof(UMLClass, classname_font) },
293 { "abstract_classname_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_classname_font) },
294 { "comment_font", PROP_TYPE_FONT, offsetof(UMLClass, comment_font) },
295
296 PROP_OFFSET_MULTICOL_COLUMN("height"),
297 { "normal_font_height", PROP_TYPE_REAL, offsetof(UMLClass, font_height) },
298 { "abstract_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_font_height) },
299 { "polymorphic_font_height", PROP_TYPE_REAL, offsetof(UMLClass, polymorphic_font_height) },
300 { "classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, classname_font_height) },
301 { "abstract_classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_classname_font_height) },
302 { "comment_font_height", PROP_TYPE_REAL, offsetof(UMLClass, comment_font_height) },
303 PROP_OFFSET_MULTICOL_END("class"),
304
305 { "operations", PROP_TYPE_DARRAY, offsetof(UMLClass , operations) },
306 { "attributes", PROP_TYPE_DARRAY, offsetof(UMLClass , attributes) } ,
307 { "templates", PROP_TYPE_DARRAY, offsetof(UMLClass , formal_params) } ,
308
309 { NULL, 0, 0 },
310 };
311
312 static void
umlclass_get_props(UMLClass * umlclass,GPtrArray * props)313 umlclass_get_props(UMLClass * umlclass, GPtrArray *props)
314 {
315 object_get_props_from_offsets(¨class->element.object,
316 umlclass_offsets, props);
317 }
318
319 static DiaMenuItem umlclass_menu_items[] = {
320 { N_("Show Comments"), umlclass_show_comments_callback, NULL,
321 DIAMENU_ACTIVE|DIAMENU_TOGGLE },
322 };
323
324 static DiaMenu umlclass_menu = {
325 N_("Class"),
326 sizeof(umlclass_menu_items)/sizeof(DiaMenuItem),
327 umlclass_menu_items,
328 NULL
329 };
330
331 DiaMenu *
umlclass_object_menu(DiaObject * obj,Point * p)332 umlclass_object_menu(DiaObject *obj, Point *p)
333 {
334 umlclass_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
335 (((UMLClass *)obj)->visible_comments?DIAMENU_TOGGLE_ON:0);
336
337 return ¨class_menu;
338 }
339
340 typedef struct _CommentState {
341 ObjectState state;
342 gboolean visible_comments;
343 } CommentState;
344 static ObjectState*
_comment_get_state(DiaObject * obj)345 _comment_get_state (DiaObject *obj)
346 {
347 CommentState *state = g_new (CommentState,1);
348 state->state.free = NULL; /* we don't have any pointers to free */
349 state->visible_comments = ((UMLClass *)obj)->visible_comments;
350 return (ObjectState *)state;
351 }
352 static void
_comment_set_state(DiaObject * obj,ObjectState * state)353 _comment_set_state (DiaObject *obj, ObjectState *state)
354 {
355 ((UMLClass *)obj)->visible_comments = ((CommentState *)state)->visible_comments;
356 g_free (state); /* rather strange convention set_state consumes the state */
357 umlclass_calculate_data((UMLClass *)obj);
358 umlclass_update_data((UMLClass *)obj);
359 }
360
361 ObjectChange *
umlclass_show_comments_callback(DiaObject * obj,Point * pos,gpointer data)362 umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data)
363 {
364 ObjectState *old_state = _comment_get_state(obj);
365 ObjectChange *change = new_object_state_change(obj, old_state, _comment_get_state, _comment_set_state );
366
367 ((UMLClass *)obj)->visible_comments = !((UMLClass *)obj)->visible_comments;
368 umlclass_calculate_data((UMLClass *)obj);
369 umlclass_update_data((UMLClass *)obj);
370 return change;
371 }
372
373 static void
umlclass_set_props(UMLClass * umlclass,GPtrArray * props)374 umlclass_set_props(UMLClass *umlclass, GPtrArray *props)
375 {
376 /* now that operations/attributes can be set here as well we need to
377 * take for the number of connections update as well
378 * Note that due to a hack in umlclass_load, this is called before
379 * the normal connection points are set up.
380 */
381 DiaObject *obj = ¨class->element.object;
382 GList *list;
383 int num;
384
385 object_set_props_from_offsets(¨class->element.object, umlclass_offsets,
386 props);
387
388 num = UMLCLASS_CONNECTIONPOINTS + umlclass_num_dynamic_connectionpoints(umlclass);
389
390 #ifdef UML_MAINPOINT
391 obj->num_connections = num + 1;
392 #else
393 obj->num_connections = num;
394 #endif
395
396 obj->connections = g_realloc(obj->connections, obj->num_connections*sizeof(ConnectionPoint *));
397
398 /* Update data: */
399 if (num > UMLCLASS_CONNECTIONPOINTS) {
400 int i;
401 /* this is just updating pointers to ConnectionPoint, the real connection handling is elsewhere.
402 * Note: Can't optimize here on number change cause the ops/attribs may have changed regardless of that.
403 */
404 i = UMLCLASS_CONNECTIONPOINTS;
405 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
406 while (list != NULL) {
407 UMLAttribute *attr = (UMLAttribute *)list->data;
408
409 uml_attribute_ensure_connection_points (attr, obj);
410 obj->connections[i] = attr->left_connection;
411 obj->connections[i]->object = obj;
412 i++;
413 obj->connections[i] = attr->right_connection;
414 obj->connections[i]->object = obj;
415 i++;
416 list = g_list_next(list);
417 }
418 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
419 while (list != NULL) {
420 UMLOperation *op = (UMLOperation *)list->data;
421
422 uml_operation_ensure_connection_points (op, obj);
423 obj->connections[i] = op->left_connection;
424 obj->connections[i]->object = obj;
425 i++;
426 obj->connections[i] = op->right_connection;
427 obj->connections[i]->object = obj;
428 i++;
429 list = g_list_next(list);
430 }
431 }
432 #ifdef UML_MAINPOINT
433 obj->connections[num] = ¨class->connections[UMLCLASS_CONNECTIONPOINTS];
434 obj->connections[num]->object = obj;
435 #endif
436
437 umlclass_calculate_data(umlclass);
438 umlclass_update_data(umlclass);
439 #ifdef DEBUG
440 /* Would like to sanity check here, but the call to object_load_props
441 * in umlclass_load means we will be called with inconsistent data. */
442 umlclass_sanity_check(umlclass, "After updating data");
443 #endif
444 }
445
446 static real
umlclass_distance_from(UMLClass * umlclass,Point * point)447 umlclass_distance_from(UMLClass *umlclass, Point *point)
448 {
449 DiaObject *obj = ¨class->element.object;
450 return distance_rectangle_point(&obj->bounding_box, point);
451 }
452
453 static void
umlclass_select(UMLClass * umlclass,Point * clicked_point,DiaRenderer * interactive_renderer)454 umlclass_select(UMLClass *umlclass, Point *clicked_point,
455 DiaRenderer *interactive_renderer)
456 {
457 element_update_handles(¨class->element);
458 }
459
460 static ObjectChange*
umlclass_move_handle(UMLClass * umlclass,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)461 umlclass_move_handle(UMLClass *umlclass, Handle *handle,
462 Point *to, ConnectionPoint *cp,
463 HandleMoveReason reason, ModifierKeys modifiers)
464 {
465 assert(umlclass!=NULL);
466 assert(handle!=NULL);
467 assert(to!=NULL);
468
469 assert(handle->id < UMLCLASS_CONNECTIONPOINTS);
470
471 return NULL;
472 }
473
474 static ObjectChange*
umlclass_move(UMLClass * umlclass,Point * to)475 umlclass_move(UMLClass *umlclass, Point *to)
476 {
477 umlclass->element.corner = *to;
478 umlclass_update_data(umlclass);
479
480 return NULL;
481 }
482 /**
483 * underlines the text at the start point using the text to determine
484 * the length of the underline. Draw a line under the text represented by
485 * string using the selected renderer, color, and line width. Since
486 * drawing this line will change the line width used by DIA, the current
487 * line width that DIA is using is also passed so it can be restored once
488 * the line has been drawn.
489 *
490 * @param renderer the renderer that will draw the line
491 * @param StartPoint the start of the line to be drawn
492 * @param font the font used to draw the text being underlined
493 * @param font_height the size in the y direction of the font used to draw the text
494 * @param string the text string that is to be underlined
495 * @param color the color of the line to draw
496 * @param line_width default line thickness
497 * @param underline_width the thickness of the line to draw
498 *
499 */
500 static void
uml_underline_text(DiaRenderer * renderer,Point StartPoint,DiaFont * font,real font_height,gchar * string,Color * color,real line_width,real underline_width)501 uml_underline_text(DiaRenderer *renderer,
502 Point StartPoint,
503 DiaFont *font,
504 real font_height,
505 gchar *string,
506 Color *color,
507 real line_width,
508 real underline_width)
509 {
510 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
511 Point UnderlineStartPoint;
512 Point UnderlineEndPoint;
513 gchar *whitespaces;
514 int first_non_whitespace = 0;
515
516 UnderlineStartPoint = StartPoint;
517 UnderlineStartPoint.y += font_height * 0.1;
518 UnderlineEndPoint = UnderlineStartPoint;
519
520 whitespaces = string;
521 while (whitespaces &&
522 g_unichar_isspace(g_utf8_get_char(whitespaces))) {
523 whitespaces = g_utf8_next_char(whitespaces);
524 }
525 first_non_whitespace = whitespaces - string;
526 whitespaces = g_strdup(string);
527 whitespaces[first_non_whitespace] = '\0';
528 UnderlineStartPoint.x += dia_font_string_width(whitespaces, font, font_height);
529 g_free(whitespaces);
530 UnderlineEndPoint.x += dia_font_string_width(string, font, font_height);
531 renderer_ops->set_linewidth(renderer, underline_width);
532 renderer_ops->draw_line(renderer, &UnderlineStartPoint, &UnderlineEndPoint, color);
533 renderer_ops->set_linewidth(renderer, line_width);
534 }
535
536 /**
537 * Create a documentation tag from a comment.
538 *
539 * First a string is created containing only the text
540 * "{documentation = ". Then the contents of the comment string
541 * are added but wrapped. This is done by first looking for any
542 * New Line characters. If the line segment is longer than the
543 * WrapPoint would allow, the line is broken at either the
544 * first whitespace before the WrapPoint or if there are no
545 * whitespaces in the segment, at the WrapPoint. This
546 * continues until the entire string has been processed and
547 * then the resulting new string is returned. No attempt is
548 * made to rejoin any of the segments, that is all New Lines
549 * are treated as hard newlines. No syllable matching is done
550 * either so breaks in words will sometimes not make real
551 * sense.
552 * <p>
553 * Finally, since this function returns newly created dynamic
554 * memory the caller must free the memory to prevent memory
555 * leaks.
556 *
557 * @param comment The comment to be wrapped to the line length limit
558 * @param WrapPoint The maximum line length allowed for the line.
559 * @param NumberOfLines The number of comment lines after the wrapping.
560 * @return a pointer to the wrapped documentation
561 *
562 * NOTE:
563 * This function should most likely be move to a source file for
564 * handling global UML functionallity at some point.
565 */
566 static gchar *
uml_create_documentation_tag(gchar * comment,gboolean tagging,gint WrapPoint,gint * NumberOfLines)567 uml_create_documentation_tag (gchar * comment,
568 gboolean tagging,
569 gint WrapPoint,
570 gint *NumberOfLines)
571 {
572 gchar *CommentTag = tagging ? "{documentation = " : "";
573 gint TagLength = strlen(CommentTag);
574 /* Make sure that there is at least some value greater then zero for the WrapPoint to
575 * support diagrams from earlier versions of Dia. So if the WrapPoint is zero then use
576 * the taglength as the WrapPoint. If the Tag has been changed such that it has a length
577 * of 0 then use 1.
578 */
579 gint WorkingWrapPoint = (TagLength<WrapPoint) ? WrapPoint : ((TagLength<=0)?1:TagLength);
580 gint RawLength = TagLength + strlen(comment) + (tagging?1:0);
581 gint MaxCookedLength = RawLength + RawLength/WorkingWrapPoint;
582 gchar *WrappedComment = g_malloc0(MaxCookedLength+1);
583 gint AvailSpace = WorkingWrapPoint - TagLength;
584 gchar *Scan;
585 gchar *BreakCandidate;
586 gunichar ScanChar;
587 gboolean AddNL = FALSE;
588
589 if (tagging)
590 strcat(WrappedComment, CommentTag);
591 *NumberOfLines = 1;
592
593 while ( *comment ) {
594 /* Skip spaces */
595 while ( *comment && g_unichar_isspace(g_utf8_get_char(comment)) ) {
596 comment = g_utf8_next_char(comment);
597 }
598 /* Copy chars */
599 if ( *comment ){
600 /* Scan to \n or avalable space exhausted */
601 Scan = comment;
602 BreakCandidate = NULL;
603 while ( *Scan && *Scan != '\n' && AvailSpace > 0 ) {
604 ScanChar = g_utf8_get_char(Scan);
605 /* We known, that g_unichar_isspace() is not recommended for word breaking;
606 * but Pango usage seems too complex.
607 */
608 if ( g_unichar_isspace(ScanChar) )
609 BreakCandidate = Scan;
610 AvailSpace--; /* not valid for nonspacing marks */
611 Scan = g_utf8_next_char(Scan);
612 }
613 if ( AvailSpace==0 && BreakCandidate != NULL )
614 Scan = BreakCandidate;
615 if ( AddNL ){
616 strcat(WrappedComment, "\n");
617 *NumberOfLines+=1;
618 }
619 AddNL = TRUE;
620 strncat(WrappedComment, comment, Scan-comment);
621 AvailSpace = WorkingWrapPoint;
622 comment = Scan;
623 }
624 }
625 if (tagging)
626 strcat(WrappedComment, "}");
627 assert(strlen(WrappedComment)<=MaxCookedLength);
628 return WrappedComment;
629 }
630
631 /**
632 * Draw the comment at the point, p, using the comment font from the
633 * class defined by umlclass. When complete update the point to reflect
634 * the size of data drawn.
635 * The comment will have been word wrapped using the function
636 * uml_create_documentation_tag, so it may have more than one line on the
637 * display.
638 *
639 * @param renderer The Renderer on which the comment is being drawn
640 * @param font The font to render the comment in.
641 * @param font_height The Y size of the font used to render the comment
642 * @param text_color A pointer to the color to use to render the comment
643 * @param comment The comment string to render
644 * @param comment_tagging If the {documentation = } tag should be enforced
645 * @param Comment_line_length The maximum length of any one line in the comment
646 * @param p The point at which the comment is to start
647 * @param alignment The method to use for alignment of the font
648 * @see uml_create_documentation
649 */
650 static void
uml_draw_comments(DiaRenderer * renderer,DiaFont * font,real font_height,Color * text_color,gchar * comment,gboolean comment_tagging,gint Comment_line_length,Point * p,gint alignment)651 uml_draw_comments(DiaRenderer *renderer,
652 DiaFont *font,
653 real font_height,
654 Color *text_color,
655 gchar *comment,
656 gboolean comment_tagging,
657 gint Comment_line_length,
658 Point *p,
659 gint alignment)
660 {
661 gint NumberOfLines = 0;
662 gint Index;
663 real ascent;
664 gchar *CommentString = 0;
665 gchar *NewLineP= NULL;
666 gchar *RenderP;
667
668 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
669
670 CommentString =
671 uml_create_documentation_tag(comment, comment_tagging, Comment_line_length, &NumberOfLines);
672 RenderP = CommentString;
673 renderer_ops->set_font(renderer, font, font_height);
674 ascent = dia_font_ascent(RenderP, font, font_height);
675 for ( Index=0; Index < NumberOfLines; Index++)
676 {
677 NewLineP = strchr(RenderP, '\n');
678 if ( NewLineP != NULL)
679 {
680 *NewLineP++ = '\0';
681 }
682 if (Index == 0) {
683 p->y += ascent;
684 }
685 else
686 {
687 p->y += font_height; /* Advance to the next line */
688 }
689 renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
690 RenderP = NewLineP;
691 if ( NewLineP == NULL){
692 break;
693 }
694 }
695 p->y += font_height - ascent;
696 g_free(CommentString);
697 }
698
699
700 /**
701 * Draw the name box of the class icon. According to the UML specification,
702 * the Name box or compartment is the top most compartment of the class
703 * icon. It may contain one or more stereotype declarations, followed by
704 * the name of the class. The name may be rendered to indicate abstraction
705 * for abstract classes. Following the name is any tagged values such as
706 * the {documentation = } tag.
707 * <p>
708 * Because the start point is the upper left of the class box, templates
709 * tend to get lost when created. By applying an offset, they will not be
710 * lost. The offset should only be added if the elem->corner.y = 0.
711 *
712 * @param umlclass The pointer to the class being drawn
713 * @param renderer The pointer to the rendering object used to draw
714 * @param elem The pointer to the element within the class to be drawn
715 * @param offset offset from start point
716 * @return The offset from the start of the class to the bottom of the namebox
717 *
718 */
719 static real
umlclass_draw_namebox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem)720 umlclass_draw_namebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem )
721 {
722 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
723 real font_height;
724 real ascent;
725 DiaFont *font;
726 Point StartPoint;
727 Point LowerRightPoint;
728 real Yoffset;
729 Color *text_color = ¨class->text_color;
730
731
732
733 StartPoint.x = elem->corner.x;
734 StartPoint.y = elem->corner.y;
735
736 Yoffset = elem->corner.y + umlclass->namebox_height;
737
738 LowerRightPoint = StartPoint;
739 LowerRightPoint.x += elem->width;
740 LowerRightPoint.y = Yoffset;
741
742 /*
743 * First draw the outer box and fill color for the class name
744 * object
745 */
746 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRightPoint, ¨class->fill_color);
747 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRightPoint, ¨class->line_color);
748
749 /* Start at the midpoint on the X axis */
750 StartPoint.x += elem->width / 2.0;
751 StartPoint.y += 0.2;
752
753 /* stereotype: */
754 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
755 gchar *String = umlclass->stereotype_string;
756 ascent = dia_font_ascent(String, umlclass->normal_font, umlclass->font_height);
757 StartPoint.y += ascent;
758 renderer_ops->set_font(renderer, umlclass->normal_font, umlclass->font_height);
759 renderer_ops->draw_string(renderer, String, &StartPoint, ALIGN_CENTER, text_color);
760 StartPoint.y += umlclass->font_height - ascent;
761 }
762
763 /* name: */
764 if (umlclass->name != NULL) {
765 if (umlclass->abstract) {
766 font = umlclass->abstract_classname_font;
767 font_height = umlclass->abstract_classname_font_height;
768 } else {
769 font = umlclass->classname_font;
770 font_height = umlclass->classname_font_height;
771 }
772 ascent = dia_font_ascent(umlclass->name, font, font_height);
773 StartPoint.y += ascent;
774
775 renderer_ops->set_font(renderer, font, font_height);
776 renderer_ops->draw_string(renderer, umlclass->name, &StartPoint, ALIGN_CENTER, text_color);
777 StartPoint.y += font_height - ascent;
778 }
779
780 /* comment */
781 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0'){
782 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
783 ¨class->text_color, umlclass->comment, umlclass->comment_tagging,
784 umlclass->comment_line_length, &StartPoint, ALIGN_CENTER);
785 }
786 return Yoffset;
787 }
788
789 /**
790 * Draw the attribute box.
791 * This attribute box follows the name box in the class icon. If the
792 * attributes are not suppress, draw each of the attributes following the
793 * UML specification for displaying attributes. Each attribute is preceded
794 * by the visibility character, +, - or # depending on whether it is public
795 * private or protected. If the attribute is "abstract" it will be rendered
796 * using the abstract font otherwise it will be rendered using the normal
797 * font. If the attribute is of class scope, static in C++, then it will be
798 * underlined. If there is a comment associated with the attribute, that is
799 * within the class description, it will be rendered as a uml comment.
800 *
801 * @param umlclass The pointer to the class being drawn
802 * @param renderer The pointer to the rendering object used to draw
803 * @param elem The pointer to the element within the class to be drawn
804 * @param Yoffset The Y offset from the start of the class at which to draw the attributebox
805 * @return The offset from the start of the class to the bottom of the attributebox
806 * @see uml_draw_comments
807 */
808 static real
umlclass_draw_attributebox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem,real Yoffset)809 umlclass_draw_attributebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
810 {
811 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
812 real font_height;
813 real ascent;
814 Point StartPoint;
815 Point LowerRight;
816 DiaFont *font;
817 Color *fill_color = ¨class->fill_color;
818 Color *line_color = ¨class->line_color;
819 Color *text_color = ¨class->text_color;
820 GList *list;
821
822 StartPoint.x = elem->corner.x;
823 StartPoint.y = Yoffset;
824 Yoffset += umlclass->attributesbox_height;
825
826 LowerRight = StartPoint;
827 LowerRight.x += elem->width;
828 LowerRight.y = Yoffset;
829
830 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
831 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
832
833 if (!umlclass->suppress_attributes) {
834 gint i = 0;
835 StartPoint.x += (umlclass->line_width/2.0 + 0.1);
836 StartPoint.y += 0.1;
837
838 list = umlclass->attributes;
839 while (list != NULL)
840 {
841 UMLAttribute *attr = (UMLAttribute *)list->data;
842 gchar *attstr = uml_get_attribute_string(attr);
843
844 if (attr->abstract) {
845 font = umlclass->abstract_font;
846 font_height = umlclass->abstract_font_height;
847 }
848 else {
849 font = umlclass->normal_font;
850 font_height = umlclass->font_height;
851 }
852 ascent = dia_font_ascent(attstr, font, font_height);
853 StartPoint.y += ascent;
854 renderer_ops->set_font (renderer, font, font_height);
855 renderer_ops->draw_string(renderer, attstr, &StartPoint, ALIGN_LEFT, text_color);
856 StartPoint.y += font_height - ascent;
857
858 if (attr->class_scope) {
859 uml_underline_text(renderer, StartPoint, font, font_height, attstr, line_color,
860 umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
861 }
862
863 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
864 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
865 ¨class->text_color, attr->comment, umlclass->comment_tagging,
866 umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
867 StartPoint.y += umlclass->comment_font_height/2;
868 }
869 list = g_list_next(list);
870 i++;
871 g_free (attstr);
872 }
873 }
874 return Yoffset;
875 }
876
877
878 /**
879 * Draw the operations box. The operations block follows the attribute box
880 * if it is visible. If the operations are not suppressed, they are
881 * displayed in the operations box. Like the attributes, operations have
882 * visibility characters, +,-, and # indicating whether the are public,
883 * private or protected. The operations are rendered in different fonts
884 * depending on whether they are abstract (pure virtual), polymorphic
885 * (virtual) or leaf (final virtual or non-virtual). The parameters to the
886 * operation may be displayed and if they are they may be conditionally
887 * wrapped to reduce horizontial size of the icon.
888 *
889 * @param umlclass The pointer to the class being drawn
890 * @param renderer The pointer to the rendering object used to draw
891 * @param elem The pointer to the element within the class to be drawn
892 * @param Yoffset The Y offset from the start of the class at which to draw the operationbox
893 * @return The offset from the start of the class to the bottom of the operationbox
894 *
895 */
896 static real
umlclass_draw_operationbox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem,real Yoffset)897 umlclass_draw_operationbox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
898 {
899 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
900 real font_height;
901 Point StartPoint;
902 Point LowerRight;
903 DiaFont *font;
904 GList *list;
905 Color *fill_color = ¨class->fill_color;
906 Color *line_color = ¨class->line_color;
907 Color *text_color = ¨class->text_color;
908
909
910 StartPoint.x = elem->corner.x;
911 StartPoint.y = Yoffset;
912 Yoffset += umlclass->operationsbox_height;
913
914 LowerRight = StartPoint;
915 LowerRight.x += elem->width;
916 LowerRight.y = Yoffset;
917
918 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
919 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
920
921 if (!umlclass->suppress_operations) {
922 gint i = 0;
923 GList *wrapsublist = NULL;
924 gchar *part_opstr = NULL;
925 int wrap_pos, last_wrap_pos, ident, wrapping_needed;
926 int part_opstr_len = 0, part_opstr_need = 0;
927
928 StartPoint.x += (umlclass->line_width/2.0 + 0.1);
929 StartPoint.y += 0.1;
930
931 list = umlclass->operations;
932 while (list != NULL) {
933 UMLOperation *op = (UMLOperation *)list->data;
934 gchar* opstr = uml_get_operation_string(op);
935 real ascent;
936
937 switch (op->inheritance_type) {
938 case UML_ABSTRACT:
939 font = umlclass->abstract_font;
940 font_height = umlclass->abstract_font_height;
941 break;
942 case UML_POLYMORPHIC:
943 font = umlclass->polymorphic_font;
944 font_height = umlclass->polymorphic_font_height;
945 break;
946 case UML_LEAF:
947 default:
948 font = umlclass->normal_font;
949 font_height = umlclass->font_height;
950 }
951
952 wrapping_needed = 0;
953 if( umlclass->wrap_operations ) {
954 wrapsublist = op->wrappos;
955 }
956
957 ascent = dia_font_ascent(opstr, font, font_height);
958 op->ascent = ascent;
959 renderer_ops->set_font(renderer, font, font_height);
960
961 if( umlclass->wrap_operations && op->needs_wrapping) {
962 ident = op->wrap_indent;
963 wrapsublist = op->wrappos;
964 wrap_pos = last_wrap_pos = 0;
965
966 while( wrapsublist != NULL) {
967 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
968
969 if( last_wrap_pos == 0) {
970 part_opstr_need = wrap_pos + 1;
971 if (part_opstr_len < part_opstr_need) {
972 part_opstr_len = part_opstr_need;
973 part_opstr = g_realloc (part_opstr, part_opstr_need);
974 }
975 strncpy( part_opstr, opstr, wrap_pos);
976 memset( part_opstr+wrap_pos, '\0', 1);
977 }
978 else {
979 part_opstr_need = ident + wrap_pos - last_wrap_pos + 1;
980 if (part_opstr_len < part_opstr_need) {
981 part_opstr_len = part_opstr_need;
982 part_opstr = g_realloc (part_opstr, part_opstr_need);
983 }
984 memset( part_opstr, ' ', ident);
985 memset( part_opstr+ident, '\0', 1);
986 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
987 }
988
989 if( last_wrap_pos == 0 ) {
990 StartPoint.y += ascent;
991 }
992 else
993 {
994 StartPoint.y += font_height;
995 }
996 renderer_ops->draw_string(renderer, part_opstr, &StartPoint, ALIGN_LEFT, text_color);
997 if (op->class_scope) {
998 uml_underline_text(renderer, StartPoint, font, font_height, part_opstr, line_color,
999 umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
1000 }
1001 last_wrap_pos = wrap_pos;
1002 wrapsublist = g_list_next( wrapsublist);
1003 }
1004 }
1005 else
1006 {
1007 StartPoint.y += ascent;
1008 renderer_ops->draw_string(renderer, opstr, &StartPoint, ALIGN_LEFT, text_color);
1009 if (op->class_scope) {
1010 uml_underline_text(renderer, StartPoint, font, font_height, opstr, line_color,
1011 umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
1012 }
1013 }
1014
1015
1016 StartPoint.y += font_height - ascent;
1017
1018 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1019 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
1020 ¨class->text_color, op->comment, umlclass->comment_tagging,
1021 umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
1022 StartPoint.y += umlclass->comment_font_height/2;
1023 }
1024
1025 list = g_list_next(list);
1026 i++;
1027 g_free (opstr);
1028 }
1029 if (part_opstr){
1030 g_free(part_opstr);
1031 }
1032 }
1033 return Yoffset;
1034 }
1035
1036 /**
1037 * Draw the template parameters box in the upper right hand corner of the
1038 * class box for paramertize classes (aka template classes). Fill in this
1039 * box with the parameters for the class.
1040 * <p>
1041 * At this time there is no provision for adding comments or documentation
1042 * to the display.
1043 *
1044 * @param umlclass The pointer to the class being drawn
1045 * @param renderer The pointer to the rendering object used to draw
1046 * @param elem The pointer to the element within the class to be drawn
1047 *
1048 */
1049 static void
umlclass_draw_template_parameters_box(UMLClass * umlclass,DiaRenderer * renderer,Element * elem)1050 umlclass_draw_template_parameters_box(UMLClass *umlclass, DiaRenderer *renderer, Element *elem)
1051 {
1052 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1053 Point UpperLeft;
1054 Point LowerRight;
1055 Point TextInsert;
1056 GList *list;
1057 gint i;
1058 DiaFont *font = umlclass->normal_font;
1059 real font_height = umlclass->font_height;
1060 real ascent;
1061 Color *fill_color = ¨class->fill_color;
1062 Color *line_color = ¨class->line_color;
1063 Color *text_color = ¨class->text_color;
1064
1065
1066 /*
1067 * Adjust for the overlay of the template on the class icon
1068 */
1069 UpperLeft.x = elem->corner.x + elem->width - UMLCLASS_TEMPLATE_OVERLAY_X;
1070 UpperLeft.y = elem->corner.y - umlclass->templates_height + UMLCLASS_TEMPLATE_OVERLAY_Y;
1071 TextInsert = UpperLeft;
1072 LowerRight = UpperLeft;
1073 LowerRight.x += umlclass->templates_width;
1074 LowerRight.y += umlclass->templates_height;
1075
1076 renderer_ops->fill_rect(renderer, &UpperLeft, &LowerRight, fill_color);
1077 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
1078 renderer_ops->set_dashlength(renderer, 0.3);
1079 renderer_ops->draw_rect(renderer, &UpperLeft, &LowerRight, line_color);
1080
1081 TextInsert.x += 0.3;
1082 TextInsert.y += 0.1;
1083 renderer_ops->set_font(renderer, font, font_height);
1084 i = 0;
1085 list = umlclass->formal_params;
1086 while (list != NULL)
1087 {
1088 gchar *paramstr = uml_get_formalparameter_string((UMLFormalParameter *)list->data);
1089
1090 ascent = dia_font_ascent(paramstr, font, font_height);
1091 TextInsert.y += ascent;
1092 renderer_ops->draw_string(renderer, paramstr, &TextInsert, ALIGN_LEFT, text_color);
1093 TextInsert.y += font_height - ascent;
1094
1095 list = g_list_next(list);
1096 i++;
1097 g_free (paramstr);
1098 }
1099 }
1100
1101 /**
1102 * Draw the class icon for the specified UMLClass object.
1103 * Set the renderer to the correct fill and line styles and the appropriate
1104 * line width. The object is drawn by the umlclass_draw_namebox,
1105 * umlclass_draw_attributebox, umlclass_draw_operationbox and
1106 * umlclass_draw_template_parameters_box.
1107 *
1108 * @param umlclass object based on the uml class that is being rendered
1109 * @param DiaRenderer renderer used to draw the object
1110 *
1111 */
1112
1113 static void
umlclass_draw(UMLClass * umlclass,DiaRenderer * renderer)1114 umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer)
1115 {
1116 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1117 real y = 0.0;
1118 Element *elem;
1119
1120 assert(umlclass != NULL);
1121 assert(renderer != NULL);
1122
1123 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
1124 renderer_ops->set_linewidth(renderer, umlclass->line_width);
1125 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
1126
1127 elem = ¨class->element;
1128
1129 y = umlclass_draw_namebox(umlclass, renderer, elem);
1130 if (umlclass->visible_attributes) {
1131 y = umlclass_draw_attributebox(umlclass, renderer, elem, y);
1132 }
1133 if (umlclass->visible_operations) {
1134 y = umlclass_draw_operationbox(umlclass, renderer, elem, y);
1135 }
1136 if (umlclass->template) {
1137 umlclass_draw_template_parameters_box(umlclass, renderer, elem);
1138 }
1139 }
1140
1141 void
umlclass_update_data(UMLClass * umlclass)1142 umlclass_update_data(UMLClass *umlclass)
1143 {
1144 Element *elem = ¨class->element;
1145 DiaObject *obj = &elem->object;
1146 real x,y;
1147 GList *list;
1148 int i;
1149 int pointswide;
1150 int lowerleftcorner;
1151 real pointspacing;
1152
1153 x = elem->corner.x;
1154 y = elem->corner.y;
1155
1156 /* Update connections: */
1157 umlclass->connections[0].pos = elem->corner;
1158 umlclass->connections[0].directions = DIR_NORTH|DIR_WEST;
1159
1160 /* there are four corner points and two side points, thus all
1161 * remaining points are on the top/bottom width
1162 */
1163 pointswide = (UMLCLASS_CONNECTIONPOINTS - 6) / 2;
1164 pointspacing = elem->width / (pointswide + 1.0);
1165
1166 /* across the top connection points */
1167 for (i=1;i<=pointswide;i++) {
1168 umlclass->connections[i].pos.x = x + (pointspacing * i);
1169 umlclass->connections[i].pos.y = y;
1170 umlclass->connections[i].directions = DIR_NORTH;
1171 }
1172
1173 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 2;
1174 umlclass->connections[i].pos.x = x + elem->width;
1175 umlclass->connections[i].pos.y = y;
1176 umlclass->connections[i].directions = DIR_NORTH|DIR_EAST;
1177
1178 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 1;
1179 umlclass->connections[i].pos.x = x;
1180 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1181 umlclass->connections[i].directions = DIR_WEST;
1182
1183 i = (UMLCLASS_CONNECTIONPOINTS / 2);
1184 umlclass->connections[i].pos.x = x + elem->width;
1185 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1186 umlclass->connections[i].directions = DIR_EAST;
1187
1188 i = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1189 umlclass->connections[i].pos.x = x;
1190 umlclass->connections[i].pos.y = y + elem->height;
1191 umlclass->connections[i].directions = DIR_WEST|DIR_SOUTH;
1192
1193 /* across the bottom connection points */
1194 lowerleftcorner = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1195 for (i=1;i<=pointswide;i++) {
1196 umlclass->connections[lowerleftcorner + i].pos.x = x + (pointspacing * i);
1197 umlclass->connections[lowerleftcorner + i].pos.y = y + elem->height;
1198 umlclass->connections[lowerleftcorner + i].directions = DIR_SOUTH;
1199 }
1200
1201 /* bottom-right corner */
1202 i = (UMLCLASS_CONNECTIONPOINTS) - 1;
1203 umlclass->connections[i].pos.x = x + elem->width;
1204 umlclass->connections[i].pos.y = y + elem->height;
1205 umlclass->connections[i].directions = DIR_EAST|DIR_SOUTH;
1206
1207 #ifdef UML_MAINPOINT
1208 /* Main point -- lives just after fixed connpoints in umlclass array */
1209 i = UMLCLASS_CONNECTIONPOINTS;
1210 umlclass->connections[i].pos.x = x + elem->width / 2;
1211 umlclass->connections[i].pos.y = y + elem->height / 2;
1212 umlclass->connections[i].directions = DIR_ALL;
1213 umlclass->connections[i].flags = CP_FLAGS_MAIN;
1214 #endif
1215
1216 y += umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1217
1218 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
1219 while (list != NULL) {
1220 UMLAttribute *attr = (UMLAttribute *)list->data;
1221
1222 attr->left_connection->pos.x = x;
1223 attr->left_connection->pos.y = y;
1224 attr->left_connection->directions = DIR_WEST;
1225 attr->right_connection->pos.x = x + elem->width;
1226 attr->right_connection->pos.y = y;
1227 attr->right_connection->directions = DIR_EAST;
1228
1229 y += umlclass->font_height;
1230 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
1231 gint NumberOfLines = 0;
1232 gchar *CommentString = 0;
1233
1234 CommentString =
1235 uml_create_documentation_tag(attr->comment, umlclass->comment_tagging, umlclass->comment_line_length, &NumberOfLines);
1236 g_free(CommentString);
1237 y += umlclass->comment_font_height*NumberOfLines + umlclass->comment_font_height/2;
1238 }
1239
1240 list = g_list_next(list);
1241 }
1242
1243 y = elem->corner.y + umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1244 if (umlclass->visible_attributes) {
1245 y += umlclass->attributesbox_height;
1246 }
1247
1248 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
1249 while (list != NULL) {
1250 UMLOperation *op = (UMLOperation *)list->data;
1251
1252 op->left_connection->pos.x = x;
1253 op->left_connection->pos.y = y;
1254 op->left_connection->directions = DIR_WEST;
1255 op->right_connection->pos.x = x + elem->width;
1256 op->right_connection->pos.y = y;
1257 op->right_connection->directions = DIR_EAST;
1258
1259 if (op->needs_wrapping) { /* Wrapped */
1260 int lines = g_list_length(op->wrappos);
1261 y += umlclass->font_height * lines;
1262 } else {
1263 y += umlclass->font_height;
1264 }
1265 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0') {
1266 gint NumberOfLines = 0;
1267 gchar *CommentString = 0;
1268
1269 CommentString =
1270 uml_create_documentation_tag(op->comment, umlclass->comment_tagging, umlclass->comment_line_length, &NumberOfLines);
1271 g_free(CommentString);
1272 y += umlclass->comment_font_height*NumberOfLines + umlclass->comment_font_height/2;
1273 }
1274 list = g_list_next(list);
1275 }
1276
1277 element_update_boundingbox(elem);
1278
1279 if (umlclass->template) {
1280 /* fix boundingumlclass for templates: */
1281 obj->bounding_box.top -= (umlclass->templates_height - UMLCLASS_TEMPLATE_OVERLAY_Y) ;
1282 obj->bounding_box.right += (umlclass->templates_width - UMLCLASS_TEMPLATE_OVERLAY_X);
1283 obj->bounding_box.left -= (elem->width < UMLCLASS_TEMPLATE_OVERLAY_X) ?
1284 (UMLCLASS_TEMPLATE_OVERLAY_X - elem->width) : 0;
1285 }
1286
1287 obj->position = elem->corner;
1288
1289 element_update_handles(elem);
1290
1291 #ifdef DEBUG
1292 umlclass_sanity_check(umlclass, "After updating data");
1293 #endif
1294 }
1295
1296 /**
1297 * Calculate the dimensions of the class icons namebox for a given object of UMLClass.
1298 * The height is stored in the class structure. When calculating the
1299 * comment, if any, the comment is word wrapped and the resulting number of
1300 * lines is then used to calculate the height of the bounding box.
1301 *
1302 * @param umlclass pointer to the object of UMLClass to calculate
1303 * @return the horizontal size of the name box.
1304 *
1305 */
1306
1307 static real
umlclass_calculate_name_data(UMLClass * umlclass)1308 umlclass_calculate_name_data(UMLClass *umlclass)
1309 {
1310 real maxwidth = 0.0;
1311 real width = 0.0;
1312 /* name box: */
1313
1314 if (umlclass->name != NULL && umlclass->name[0] != '\0') {
1315 if (umlclass->abstract) {
1316 maxwidth = dia_font_string_width(umlclass->name,
1317 umlclass->abstract_classname_font,
1318 umlclass->abstract_classname_font_height);
1319 } else {
1320 maxwidth = dia_font_string_width(umlclass->name,
1321 umlclass->classname_font,
1322 umlclass->classname_font_height);
1323 }
1324 }
1325
1326 umlclass->namebox_height = umlclass->classname_font_height + 4*0.1;
1327 if (umlclass->stereotype_string != NULL) {
1328 g_free(umlclass->stereotype_string);
1329 }
1330 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
1331 umlclass->namebox_height += umlclass->font_height;
1332 umlclass->stereotype_string = g_strconcat ( UML_STEREOTYPE_START,
1333 umlclass->stereotype,
1334 UML_STEREOTYPE_END,
1335 NULL);
1336
1337 width = dia_font_string_width (umlclass->stereotype_string,
1338 umlclass->normal_font,
1339 umlclass->font_height);
1340 maxwidth = MAX(width, maxwidth);
1341 } else {
1342 umlclass->stereotype_string = NULL;
1343 }
1344
1345 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0')
1346 {
1347 int NumberOfLines = 0;
1348 gchar *CommentString = uml_create_documentation_tag (umlclass->comment,
1349 umlclass->comment_tagging,
1350 umlclass->comment_line_length,
1351 &NumberOfLines);
1352 width = dia_font_string_width (CommentString,
1353 umlclass->comment_font,
1354 umlclass->comment_font_height);
1355
1356 g_free(CommentString);
1357 umlclass->namebox_height += umlclass->comment_font_height * NumberOfLines;
1358 maxwidth = MAX(width, maxwidth);
1359 }
1360 return maxwidth;
1361 }
1362
1363 /**
1364 * Calculate the dimensions of the attribute box on an object of type UMLClass.
1365 * @param umlclass a pointer to an object of UMLClass
1366 * @return the horizontal size of the attribute box
1367 *
1368 */
1369
1370 static real
umlclass_calculate_attribute_data(UMLClass * umlclass)1371 umlclass_calculate_attribute_data(UMLClass *umlclass)
1372 {
1373 int i;
1374 real maxwidth = 0.0;
1375 real width = 0.0;
1376 GList *list;
1377
1378 umlclass->attributesbox_height = 2*0.1;
1379
1380 if (g_list_length(umlclass->attributes) != 0)
1381 {
1382 i = 0;
1383 list = umlclass->attributes;
1384 while (list != NULL)
1385 {
1386 UMLAttribute *attr = (UMLAttribute *) list->data;
1387 gchar *attstr = uml_get_attribute_string(attr);
1388
1389 if (attr->abstract)
1390 {
1391 width = dia_font_string_width(attstr,
1392 umlclass->abstract_font,
1393 umlclass->abstract_font_height);
1394 umlclass->attributesbox_height += umlclass->abstract_font_height;
1395 }
1396 else
1397 {
1398 width = dia_font_string_width(attstr,
1399 umlclass->normal_font,
1400 umlclass->font_height);
1401 umlclass->attributesbox_height += umlclass->font_height;
1402 }
1403 maxwidth = MAX(width, maxwidth);
1404
1405 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1406 {
1407 int NumberOfLines = 0;
1408 gchar *CommentString = uml_create_documentation_tag(attr->comment,
1409 umlclass->comment_tagging,
1410 umlclass->comment_line_length,
1411 &NumberOfLines);
1412 width = dia_font_string_width(CommentString,
1413 umlclass->comment_font,
1414 umlclass->comment_font_height);
1415 g_free(CommentString);
1416 umlclass->attributesbox_height += umlclass->comment_font_height * NumberOfLines + umlclass->comment_font_height/2;
1417 maxwidth = MAX(width, maxwidth);
1418 }
1419
1420 i++;
1421 list = g_list_next(list);
1422 g_free (attstr);
1423 }
1424 }
1425
1426 if ((umlclass->attributesbox_height<0.4)|| umlclass->suppress_attributes )
1427 {
1428 umlclass->attributesbox_height = 0.4;
1429 }
1430 return maxwidth;
1431 }
1432
1433 /**
1434 * Calculate the dimensions of the operations box of an object of UMLClass.
1435 * The vertical size or height is stored in the object.
1436 * @param umlclass a pointer to an object of UMLClass
1437 * @return the horizontial size of the operations box
1438 *
1439 */
1440
1441 static real
umlclass_calculate_operation_data(UMLClass * umlclass)1442 umlclass_calculate_operation_data(UMLClass *umlclass)
1443 {
1444 int i;
1445 int pos_next_comma;
1446 int pos_brace;
1447 int wrap_pos;
1448 int last_wrap_pos;
1449 int indent;
1450 int offset;
1451 int maxlinewidth;
1452 int length;
1453 real maxwidth = 0.0;
1454 real width = 0.0;
1455 GList *list;
1456 GList *wrapsublist;
1457
1458 /* operations box: */
1459 umlclass->operationsbox_height = 2*0.1;
1460
1461 if (0 != g_list_length(umlclass->operations))
1462 {
1463 i = 0;
1464 list = umlclass->operations;
1465 while (list != NULL)
1466 {
1467 UMLOperation *op = (UMLOperation *) list->data;
1468 gchar *opstr = uml_get_operation_string(op);
1469 DiaFont *Font;
1470 real FontHeight;
1471
1472 length = strlen( (const gchar*)opstr);
1473
1474 if (op->wrappos != NULL) {
1475 g_list_free(op->wrappos);
1476 }
1477 op->wrappos = NULL;
1478
1479 switch(op->inheritance_type)
1480 {
1481 case UML_ABSTRACT:
1482 Font = umlclass->abstract_font;
1483 FontHeight = umlclass->abstract_font_height;
1484 break;
1485 case UML_POLYMORPHIC:
1486 Font = umlclass->polymorphic_font;
1487 FontHeight = umlclass->polymorphic_font_height;
1488 break;
1489 case UML_LEAF:
1490 default:
1491 Font = umlclass->normal_font;
1492 FontHeight = umlclass->font_height;
1493 }
1494 op->ascent = dia_font_ascent(opstr, Font, FontHeight);
1495
1496 if( umlclass->wrap_operations )
1497 {
1498 if( length > umlclass->wrap_after_char)
1499 {
1500 gchar *part_opstr;
1501 op->needs_wrapping = TRUE;
1502
1503 /* count maximal line width to create a secure buffer (part_opstr)
1504 and build the sublist with the wrapping data for the current operation, which will be used by umlclass_draw(), too.
1505 */
1506 pos_next_comma = pos_brace = wrap_pos = offset
1507 = maxlinewidth = umlclass->max_wrapped_line_width = 0;
1508 while( wrap_pos + offset < length)
1509 {
1510 do
1511 {
1512 pos_next_comma = strcspn( (const gchar*)opstr + wrap_pos + offset, ",");
1513 wrap_pos += pos_next_comma + 1;
1514 } while( wrap_pos < umlclass->wrap_after_char - pos_brace
1515 && wrap_pos + offset < length);
1516
1517 if( offset == 0){
1518 pos_brace = strcspn( opstr, "(");
1519 op->wrap_indent = pos_brace + 1;
1520 }
1521 op->wrappos = g_list_append(op->wrappos,
1522 GINT_TO_POINTER(wrap_pos + offset));
1523
1524 maxlinewidth = MAX(maxlinewidth, wrap_pos);
1525
1526 offset += wrap_pos;
1527 wrap_pos = 0;
1528 }
1529 umlclass->max_wrapped_line_width = MAX( umlclass->max_wrapped_line_width, maxlinewidth+1);
1530
1531 indent = op->wrap_indent;
1532 part_opstr = g_alloca(umlclass->max_wrapped_line_width+indent+1);
1533
1534 wrapsublist = op->wrappos;
1535 wrap_pos = last_wrap_pos = 0;
1536
1537 while( wrapsublist != NULL){
1538 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
1539 if( last_wrap_pos == 0){
1540 strncpy( part_opstr, opstr, wrap_pos);
1541 memset( part_opstr+wrap_pos, '\0', 1);
1542 }
1543 else
1544 {
1545 memset( part_opstr, ' ', indent);
1546 memset( part_opstr+indent, '\0', 1);
1547 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
1548 }
1549
1550 width = dia_font_string_width(part_opstr,Font,FontHeight);
1551 umlclass->operationsbox_height += FontHeight;
1552
1553 maxwidth = MAX(width, maxwidth);
1554 last_wrap_pos = wrap_pos;
1555 wrapsublist = g_list_next( wrapsublist);
1556 }
1557 }
1558 else
1559 {
1560 op->needs_wrapping = FALSE;
1561 }
1562 }
1563
1564 if (!(umlclass->wrap_operations && length > umlclass->wrap_after_char)) {
1565 switch(op->inheritance_type)
1566 {
1567 case UML_ABSTRACT:
1568 Font = umlclass->abstract_font;
1569 FontHeight = umlclass->abstract_font_height;
1570 break;
1571 case UML_POLYMORPHIC:
1572 Font = umlclass->polymorphic_font;
1573 FontHeight = umlclass->polymorphic_font_height;
1574 break;
1575 case UML_LEAF:
1576 default:
1577 Font = umlclass->normal_font;
1578 FontHeight = umlclass->font_height;
1579 }
1580 width = dia_font_string_width(opstr,Font,FontHeight);
1581 umlclass->operationsbox_height += FontHeight;
1582
1583 maxwidth = MAX(width, maxwidth);
1584 }
1585
1586 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1587 int NumberOfLines = 0;
1588 gchar *CommentString = uml_create_documentation_tag(op->comment,
1589 umlclass->comment_tagging,
1590 umlclass->comment_line_length,
1591 &NumberOfLines);
1592 width = dia_font_string_width(CommentString,
1593 umlclass->comment_font,
1594 umlclass->comment_font_height);
1595 g_free(CommentString);
1596 umlclass->operationsbox_height += umlclass->comment_font_height * NumberOfLines + umlclass->comment_font_height/2;
1597 maxwidth = MAX(width, maxwidth);
1598 }
1599
1600 i++;
1601 list = g_list_next(list);
1602 g_free (opstr);
1603 }
1604 }
1605
1606 umlclass->element.width = maxwidth + 2*0.3;
1607
1608 if ((umlclass->operationsbox_height<0.4) || umlclass->suppress_operations ) {
1609 umlclass->operationsbox_height = 0.4;
1610 }
1611
1612 return maxwidth;
1613 }
1614
1615 /**
1616 * calculate the size of the class icon for an object of UMLClass.
1617 * This is done by calculating the size of the text to be displayed within
1618 * each of the contained bounding boxes, name, attributes and operations.
1619 * Because the comments may require wrapping, each comment is wrapped and
1620 * the resulting number of lines is used to calculate the size of the
1621 * comment within the box. The various font settings with in the class
1622 * properties contribute to the overall size of the resulting bounding box.
1623 *
1624 * * @param umlclass a pointer to an object of UMLClass
1625 *
1626 */
1627 void
umlclass_calculate_data(UMLClass * umlclass)1628 umlclass_calculate_data(UMLClass *umlclass)
1629 {
1630 int i;
1631 int num_templates;
1632 real maxwidth = 0.0;
1633 real width;
1634 GList *list;
1635
1636 if (!umlclass->destroyed)
1637 {
1638 maxwidth = MAX(umlclass_calculate_name_data(umlclass), maxwidth);
1639
1640 umlclass->element.height = umlclass->namebox_height;
1641
1642 if (umlclass->visible_attributes){
1643 maxwidth = MAX(umlclass_calculate_attribute_data(umlclass), maxwidth);
1644 umlclass->element.height += umlclass->attributesbox_height;
1645 }
1646 if (umlclass->visible_operations){
1647 maxwidth = MAX(umlclass_calculate_operation_data(umlclass), maxwidth);
1648 umlclass->element.height += umlclass->operationsbox_height;
1649 }
1650 umlclass->element.width = maxwidth+0.5;
1651 /* templates box: */
1652 num_templates = g_list_length(umlclass->formal_params);
1653
1654 umlclass->templates_height =
1655 umlclass->font_height * num_templates + 2*0.1;
1656 umlclass->templates_height = MAX(umlclass->templates_height, 0.4);
1657
1658
1659 maxwidth = UMLCLASS_TEMPLATE_OVERLAY_X;
1660 if (num_templates != 0)
1661 {
1662 i = 0;
1663 list = umlclass->formal_params;
1664 while (list != NULL)
1665 {
1666 UMLFormalParameter *param = (UMLFormalParameter *) list->data;
1667 gchar *paramstr = uml_get_formalparameter_string(param);
1668
1669 width = dia_font_string_width(paramstr,
1670 umlclass->normal_font,
1671 umlclass->font_height);
1672 maxwidth = MAX(width, maxwidth);
1673
1674 i++;
1675 list = g_list_next(list);
1676 g_free (paramstr);
1677 }
1678 }
1679 umlclass->templates_width = maxwidth + 2*0.2;
1680 }
1681 }
1682
1683 static void
fill_in_fontdata(UMLClass * umlclass)1684 fill_in_fontdata(UMLClass *umlclass)
1685 {
1686 if (umlclass->normal_font == NULL) {
1687 umlclass->font_height = 0.8;
1688 umlclass->normal_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, 0.8);
1689 }
1690 if (umlclass->abstract_font == NULL) {
1691 umlclass->abstract_font_height = 0.8;
1692 umlclass->abstract_font =
1693 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD, 0.8);
1694 }
1695 if (umlclass->polymorphic_font == NULL) {
1696 umlclass->polymorphic_font_height = 0.8;
1697 umlclass->polymorphic_font =
1698 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC, 0.8);
1699 }
1700 if (umlclass->classname_font == NULL) {
1701 umlclass->classname_font_height = 1.0;
1702 umlclass->classname_font =
1703 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD, 1.0);
1704 }
1705 if (umlclass->abstract_classname_font == NULL) {
1706 umlclass->abstract_classname_font_height = 1.0;
1707 umlclass->abstract_classname_font =
1708 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC, 1.0);
1709 }
1710 if (umlclass->comment_font == NULL) {
1711 umlclass->comment_font_height = 0.7;
1712 umlclass->comment_font = dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1713 }
1714 }
1715 /**
1716 * Create an object of type class
1717 * By default this will create a object of class UMLClass. Howerver there
1718 * are at least two types of UMLClass objects, so the user_data is selects
1719 * the correct UMLClass object. Other than that this is quite straight
1720 * forward. The key to the polymorphic nature of this object is the use of
1721 * the DiaObjectType record which in conjunction with the user_data
1722 * controls the specific derived object type.
1723 *
1724 * @param startpoint the origin of the object being created
1725 * @param user_data Information used by this routine to create the appropriate object
1726 * @param handle1 ignored when creating a class object
1727 * @param handle2 ignored when creating a class object
1728 * @return a pointer to the object created
1729 *
1730 * NOTE:
1731 * This function should most likely be move to a source file for
1732 * handling global UML functionallity at some point.
1733 */
1734
1735 static DiaObject *
umlclass_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)1736 umlclass_create(Point *startpoint,
1737 void *user_data,
1738 Handle **handle1,
1739 Handle **handle2)
1740 {
1741 UMLClass *umlclass;
1742 Element *elem;
1743 DiaObject *obj;
1744 int i;
1745
1746 umlclass = g_malloc0(sizeof(UMLClass));
1747 elem = ¨class->element;
1748 obj = &elem->object;
1749
1750
1751 elem->corner = *startpoint;
1752
1753 #ifdef UML_MAINPOINT
1754 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1); /* No attribs or ops => 0 extra connectionpoints. */
1755 #else
1756 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS); /* No attribs or ops => 0 extra connectionpoints. */
1757 #endif
1758
1759 umlclass->properties_dialog = NULL;
1760 fill_in_fontdata(umlclass);
1761
1762
1763 /*
1764 * The following block of code may need to be converted to a switch statement if more than
1765 * two types of objects can be made - Dave Klotzbach
1766 */
1767 umlclass->template = (GPOINTER_TO_INT(user_data)==1);
1768
1769 if (umlclass->template){
1770 umlclass->name = g_strdup (_("Template"));
1771 }
1772 else {
1773 umlclass->name = g_strdup (_("Class"));
1774 }
1775 obj->type = ¨class_type;
1776 obj->ops = ¨class_ops;
1777
1778 umlclass->stereotype = NULL;
1779 umlclass->comment = NULL;
1780
1781 umlclass->abstract = FALSE;
1782
1783 umlclass->suppress_attributes = FALSE;
1784 umlclass->suppress_operations = FALSE;
1785
1786 umlclass->visible_attributes = TRUE;
1787 umlclass->visible_operations = TRUE;
1788 umlclass->visible_comments = FALSE;
1789
1790 umlclass->wrap_operations = TRUE;
1791 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
1792
1793 umlclass->attributes = NULL;
1794
1795 umlclass->operations = NULL;
1796
1797 umlclass->formal_params = NULL;
1798
1799 umlclass->stereotype_string = NULL;
1800
1801 umlclass->line_width = attributes_get_default_linewidth();
1802 umlclass->text_color = color_black;
1803 umlclass->line_color = attributes_get_foreground();
1804 umlclass->fill_color = attributes_get_background();
1805
1806 umlclass_calculate_data(umlclass);
1807
1808 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1809 obj->connections[i] = ¨class->connections[i];
1810 umlclass->connections[i].object = obj;
1811 umlclass->connections[i].connected = NULL;
1812 }
1813 #ifdef UML_MAINPOINT
1814 /* Put mainpoint at the end, after conditional attr/oprn points,
1815 * but store it in the local connectionpoint array. */
1816 i += umlclass_num_dynamic_connectionpoints(umlclass);
1817 obj->connections[i] = ¨class->connections[UMLCLASS_CONNECTIONPOINTS];
1818 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = obj;
1819 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1820 #endif
1821
1822 elem->extra_spacing.border_trans = umlclass->line_width/2.0;
1823 umlclass_update_data(umlclass);
1824
1825 for (i=0;i<8;i++) {
1826 obj->handles[i]->type = HANDLE_NON_MOVABLE;
1827 }
1828
1829 *handle1 = NULL;
1830 *handle2 = NULL;
1831 return ¨class->element.object;
1832 }
1833
1834 static void
umlclass_destroy(UMLClass * umlclass)1835 umlclass_destroy(UMLClass *umlclass)
1836 {
1837 GList *list;
1838 UMLAttribute *attr;
1839 UMLOperation *op;
1840 UMLFormalParameter *param;
1841
1842 #ifdef DEBUG
1843 umlclass_sanity_check(umlclass, "Destroying");
1844 #endif
1845
1846 umlclass->destroyed = TRUE;
1847
1848 dia_font_unref(umlclass->normal_font);
1849 dia_font_unref(umlclass->abstract_font);
1850 dia_font_unref(umlclass->polymorphic_font);
1851 dia_font_unref(umlclass->classname_font);
1852 dia_font_unref(umlclass->abstract_classname_font);
1853 dia_font_unref(umlclass->comment_font);
1854
1855 element_destroy(¨class->element);
1856
1857 g_free(umlclass->name);
1858 g_free(umlclass->stereotype);
1859 g_free(umlclass->comment);
1860
1861 list = umlclass->attributes;
1862 while (list != NULL) {
1863 attr = (UMLAttribute *)list->data;
1864 g_free(attr->left_connection);
1865 g_free(attr->right_connection);
1866 uml_attribute_destroy(attr);
1867 list = g_list_next(list);
1868 }
1869 g_list_free(umlclass->attributes);
1870
1871 list = umlclass->operations;
1872 while (list != NULL) {
1873 op = (UMLOperation *)list->data;
1874 g_free(op->left_connection);
1875 g_free(op->right_connection);
1876 uml_operation_destroy(op);
1877 list = g_list_next(list);
1878 }
1879 g_list_free(umlclass->operations);
1880
1881 list = umlclass->formal_params;
1882 while (list != NULL) {
1883 param = (UMLFormalParameter *)list->data;
1884 uml_formalparameter_destroy(param);
1885 list = g_list_next(list);
1886 }
1887 g_list_free(umlclass->formal_params);
1888
1889 if (umlclass->stereotype_string != NULL) {
1890 g_free(umlclass->stereotype_string);
1891 }
1892
1893 if (umlclass->properties_dialog != NULL) {
1894 umlclass_dialog_free (umlclass->properties_dialog);
1895 }
1896 }
1897
1898 static DiaObject *
umlclass_copy(UMLClass * umlclass)1899 umlclass_copy(UMLClass *umlclass)
1900 {
1901 int i;
1902 UMLClass *newumlclass;
1903 Element *elem, *newelem;
1904 DiaObject *newobj;
1905 GList *list;
1906 UMLFormalParameter *param;
1907
1908 elem = ¨class->element;
1909
1910 newumlclass = g_malloc0(sizeof(UMLClass));
1911 newelem = &newumlclass->element;
1912 newobj = &newelem->object;
1913
1914 element_copy(elem, newelem);
1915
1916 newumlclass->font_height = umlclass->font_height;
1917 newumlclass->abstract_font_height = umlclass->abstract_font_height;
1918 newumlclass->polymorphic_font_height = umlclass->polymorphic_font_height;
1919 newumlclass->classname_font_height = umlclass->classname_font_height;
1920 newumlclass->abstract_classname_font_height =
1921 umlclass->abstract_classname_font_height;
1922 newumlclass->comment_font_height =
1923 umlclass->comment_font_height;
1924
1925 newumlclass->normal_font =
1926 dia_font_copy(umlclass->normal_font);
1927 newumlclass->abstract_font =
1928 dia_font_copy(umlclass->abstract_font);
1929 newumlclass->polymorphic_font =
1930 dia_font_copy(umlclass->polymorphic_font);
1931 newumlclass->classname_font =
1932 dia_font_copy(umlclass->classname_font);
1933 newumlclass->abstract_classname_font =
1934 dia_font_copy(umlclass->abstract_classname_font);
1935 newumlclass->comment_font =
1936 dia_font_copy(umlclass->comment_font);
1937
1938 newumlclass->name = g_strdup(umlclass->name);
1939 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0')
1940 newumlclass->stereotype = g_strdup(umlclass->stereotype);
1941 else
1942 newumlclass->stereotype = NULL;
1943
1944 if (umlclass->comment != NULL)
1945 newumlclass->comment = g_strdup(umlclass->comment);
1946 else
1947 newumlclass->comment = NULL;
1948
1949 newumlclass->abstract = umlclass->abstract;
1950 newumlclass->suppress_attributes = umlclass->suppress_attributes;
1951 newumlclass->suppress_operations = umlclass->suppress_operations;
1952 newumlclass->visible_attributes = umlclass->visible_attributes;
1953 newumlclass->visible_operations = umlclass->visible_operations;
1954 newumlclass->visible_comments = umlclass->visible_comments;
1955 newumlclass->wrap_operations = umlclass->wrap_operations;
1956 newumlclass->wrap_after_char = umlclass->wrap_after_char;
1957 newumlclass->comment_line_length = umlclass->comment_line_length;
1958 newumlclass->comment_tagging = umlclass->comment_tagging;
1959 newumlclass->line_width = umlclass->line_width;
1960 newumlclass->text_color = umlclass->text_color;
1961 newumlclass->line_color = umlclass->line_color;
1962 newumlclass->fill_color = umlclass->fill_color;
1963
1964 newumlclass->attributes = NULL;
1965 list = umlclass->attributes;
1966 while (list != NULL) {
1967 UMLAttribute *attr = (UMLAttribute *)list->data;
1968 /* not copying the connection, if there was one */
1969 UMLAttribute *newattr = uml_attribute_copy(attr);
1970 uml_attribute_ensure_connection_points (newattr, newobj);
1971
1972 newumlclass->attributes = g_list_append(newumlclass->attributes,
1973 newattr);
1974 list = g_list_next(list);
1975 }
1976
1977 newumlclass->operations = NULL;
1978 list = umlclass->operations;
1979 while (list != NULL) {
1980 UMLOperation *op = (UMLOperation *)list->data;
1981 UMLOperation *newop = uml_operation_copy(op);
1982 uml_operation_ensure_connection_points (newop, newobj);
1983
1984 newumlclass->operations = g_list_append(newumlclass->operations,
1985 newop);
1986 list = g_list_next(list);
1987 }
1988
1989 newumlclass->template = umlclass->template;
1990
1991 newumlclass->formal_params = NULL;
1992 list = umlclass->formal_params;
1993 while (list != NULL) {
1994 param = (UMLFormalParameter *)list->data;
1995 newumlclass->formal_params =
1996 g_list_append(newumlclass->formal_params,
1997 uml_formalparameter_copy(param));
1998 list = g_list_next(list);
1999 }
2000
2001 newumlclass->properties_dialog = NULL;
2002
2003 newumlclass->stereotype_string = NULL;
2004
2005 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2006 newobj->connections[i] = &newumlclass->connections[i];
2007 newumlclass->connections[i].object = newobj;
2008 newumlclass->connections[i].connected = NULL;
2009 newumlclass->connections[i].pos = umlclass->connections[i].pos;
2010 newumlclass->connections[i].last_pos = umlclass->connections[i].last_pos;
2011 }
2012
2013 umlclass_calculate_data(newumlclass);
2014
2015 i = UMLCLASS_CONNECTIONPOINTS;
2016 if ( (newumlclass->visible_attributes) &&
2017 (!newumlclass->suppress_attributes)) {
2018 list = newumlclass->attributes;
2019 while (list != NULL) {
2020 UMLAttribute *attr = (UMLAttribute *)list->data;
2021 newobj->connections[i++] = attr->left_connection;
2022 newobj->connections[i++] = attr->right_connection;
2023
2024 list = g_list_next(list);
2025 }
2026 }
2027
2028 if ( (newumlclass->visible_operations) &&
2029 (!newumlclass->suppress_operations)) {
2030 list = newumlclass->operations;
2031 while (list != NULL) {
2032 UMLOperation *op = (UMLOperation *)list->data;
2033 newobj->connections[i++] = op->left_connection;
2034 newobj->connections[i++] = op->right_connection;
2035
2036 list = g_list_next(list);
2037 }
2038 }
2039
2040 #ifdef UML_MAINPOINT
2041 newobj->connections[i] = &newumlclass->connections[UMLCLASS_CONNECTIONPOINTS];
2042 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = newobj;
2043 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
2044 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos =
2045 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos;
2046 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos =
2047 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos;
2048 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags =
2049 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags;
2050 i++;
2051 #endif
2052
2053 umlclass_update_data(newumlclass);
2054
2055 #ifdef DEBUG
2056 umlclass_sanity_check(newumlclass, "Copied");
2057 #endif
2058
2059 return &newumlclass->element.object;
2060 }
2061
2062
2063 static void
umlclass_save(UMLClass * umlclass,ObjectNode obj_node,const char * filename)2064 umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
2065 const char *filename)
2066 {
2067 UMLAttribute *attr;
2068 UMLOperation *op;
2069 UMLFormalParameter *formal_param;
2070 GList *list;
2071 AttributeNode attr_node;
2072
2073 #ifdef DEBUG
2074 umlclass_sanity_check(umlclass, "Saving");
2075 #endif
2076
2077 element_save(¨class->element, obj_node);
2078
2079 /* Class info: */
2080 data_add_string(new_attribute(obj_node, "name"),
2081 umlclass->name);
2082 data_add_string(new_attribute(obj_node, "stereotype"),
2083 umlclass->stereotype);
2084 data_add_string(new_attribute(obj_node, "comment"),
2085 umlclass->comment);
2086 data_add_boolean(new_attribute(obj_node, "abstract"),
2087 umlclass->abstract);
2088 data_add_boolean(new_attribute(obj_node, "suppress_attributes"),
2089 umlclass->suppress_attributes);
2090 data_add_boolean(new_attribute(obj_node, "suppress_operations"),
2091 umlclass->suppress_operations);
2092 data_add_boolean(new_attribute(obj_node, "visible_attributes"),
2093 umlclass->visible_attributes);
2094 data_add_boolean(new_attribute(obj_node, "visible_operations"),
2095 umlclass->visible_operations);
2096 data_add_boolean(new_attribute(obj_node, "visible_comments"),
2097 umlclass->visible_comments);
2098 data_add_boolean(new_attribute(obj_node, "wrap_operations"),
2099 umlclass->wrap_operations);
2100 data_add_int(new_attribute(obj_node, "wrap_after_char"),
2101 umlclass->wrap_after_char);
2102 data_add_int(new_attribute(obj_node, "comment_line_length"),
2103 umlclass->comment_line_length);
2104 data_add_boolean(new_attribute(obj_node, "comment_tagging"),
2105 umlclass->comment_tagging);
2106 data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
2107 umlclass->line_width);
2108 data_add_color(new_attribute(obj_node, "line_color"),
2109 ¨class->line_color);
2110 data_add_color(new_attribute(obj_node, "fill_color"),
2111 ¨class->fill_color);
2112 data_add_color(new_attribute(obj_node, "text_color"),
2113 ¨class->text_color);
2114 data_add_font (new_attribute (obj_node, "normal_font"),
2115 umlclass->normal_font);
2116 data_add_font (new_attribute (obj_node, "abstract_font"),
2117 umlclass->abstract_font);
2118 data_add_font (new_attribute (obj_node, "polymorphic_font"),
2119 umlclass->polymorphic_font);
2120 data_add_font (new_attribute (obj_node, "classname_font"),
2121 umlclass->classname_font);
2122 data_add_font (new_attribute (obj_node, "abstract_classname_font"),
2123 umlclass->abstract_classname_font);
2124 data_add_font (new_attribute (obj_node, "comment_font"),
2125 umlclass->comment_font);
2126 data_add_real (new_attribute (obj_node, "normal_font_height"),
2127 umlclass->font_height);
2128 data_add_real (new_attribute (obj_node, "polymorphic_font_height"),
2129 umlclass->polymorphic_font_height);
2130 data_add_real (new_attribute (obj_node, "abstract_font_height"),
2131 umlclass->abstract_font_height);
2132 data_add_real (new_attribute (obj_node, "classname_font_height"),
2133 umlclass->classname_font_height);
2134 data_add_real (new_attribute (obj_node, "abstract_classname_font_height"),
2135 umlclass->abstract_classname_font_height);
2136 data_add_real (new_attribute (obj_node, "comment_font_height"),
2137 umlclass->comment_font_height);
2138
2139 /* Attribute info: */
2140 attr_node = new_attribute(obj_node, "attributes");
2141 list = umlclass->attributes;
2142 while (list != NULL) {
2143 attr = (UMLAttribute *) list->data;
2144 uml_attribute_write(attr_node, attr);
2145 list = g_list_next(list);
2146 }
2147
2148 /* Operations info: */
2149 attr_node = new_attribute(obj_node, "operations");
2150 list = umlclass->operations;
2151 while (list != NULL) {
2152 op = (UMLOperation *) list->data;
2153 uml_operation_write(attr_node, op);
2154 list = g_list_next(list);
2155 }
2156
2157 /* Template info: */
2158 data_add_boolean(new_attribute(obj_node, "template"),
2159 umlclass->template);
2160
2161 attr_node = new_attribute(obj_node, "templates");
2162 list = umlclass->formal_params;
2163 while (list != NULL) {
2164 formal_param = (UMLFormalParameter *) list->data;
2165 uml_formalparameter_write(attr_node, formal_param);
2166 list = g_list_next(list);
2167 }
2168 }
2169
umlclass_load(ObjectNode obj_node,int version,const char * filename)2170 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
2171 const char *filename)
2172 {
2173 UMLClass *umlclass;
2174 Element *elem;
2175 DiaObject *obj;
2176 AttributeNode attr_node;
2177 int i;
2178 GList *list;
2179
2180
2181 umlclass = g_malloc0(sizeof(UMLClass));
2182 elem = ¨class->element;
2183 obj = &elem->object;
2184
2185 obj->type = ¨class_type;
2186 obj->ops = ¨class_ops;
2187
2188 element_load(elem, obj_node);
2189
2190 #ifdef UML_MAINPOINT
2191 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1);
2192 #else
2193 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS);
2194 #endif
2195
2196 umlclass->properties_dialog = NULL;
2197
2198 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2199 obj->connections[i] = ¨class->connections[i];
2200 umlclass->connections[i].object = obj;
2201 umlclass->connections[i].connected = NULL;
2202 }
2203
2204 fill_in_fontdata(umlclass);
2205
2206 /* kind of dirty, object_load_props() may leave us in an inconsistent state --hb */
2207 object_load_props(obj,obj_node);
2208
2209 /* parameters loaded via StdProp dont belong here anymore. In case of strings they
2210 * will produce leaks. Otherwise the are just wasteing time (at runtime and while
2211 * reading the code). Except maybe for some compatibility stuff.
2212 * Although that *could* probably done via StdProp too. --hb
2213 */
2214
2215 /* new since 0.94, don't wrap by default to keep old diagrams intact */
2216 umlclass->wrap_operations = FALSE;
2217 attr_node = object_find_attribute(obj_node, "wrap_operations");
2218 if (attr_node != NULL)
2219 umlclass->wrap_operations = data_boolean(attribute_first_data(attr_node));
2220
2221 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
2222 attr_node = object_find_attribute(obj_node, "wrap_after_char");
2223 if (attr_node != NULL)
2224 umlclass->wrap_after_char = data_int(attribute_first_data(attr_node));
2225
2226 /* if it uses the new name the value is already set by object_load_props() above */
2227 umlclass->comment_line_length = UMLCLASS_COMMENT_LINE_LENGTH;
2228 attr_node = object_find_attribute(obj_node,"comment_line_length");
2229 /* support the unusal cased name, although it only existed in cvs version */
2230 if (attr_node == NULL)
2231 attr_node = object_find_attribute(obj_node,"Comment_line_length");
2232 if (attr_node != NULL)
2233 umlclass->comment_line_length = data_int(attribute_first_data(attr_node));
2234
2235 /* compatibility with 0.94 and before as well as the temporary state with only 'comment_line_length' */
2236 umlclass->comment_tagging = (attr_node != NULL);
2237 attr_node = object_find_attribute(obj_node, "comment_tagging");
2238 if (attr_node != NULL)
2239 umlclass->comment_tagging = data_boolean(attribute_first_data(attr_node));
2240
2241 /* Loads the line width */
2242 umlclass->line_width = UMLCLASS_BORDER;
2243 attr_node = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
2244 if(attr_node != NULL)
2245 umlclass->line_width = data_real(attribute_first_data(attr_node));
2246
2247 umlclass->line_color = color_black;
2248 /* support the old name ... */
2249 attr_node = object_find_attribute(obj_node, "foreground_color");
2250 if(attr_node != NULL)
2251 data_color(attribute_first_data(attr_node), ¨class->line_color);
2252 umlclass->text_color = umlclass->line_color;
2253 /* ... but prefer the new one */
2254 attr_node = object_find_attribute(obj_node, "line_color");
2255 if(attr_node != NULL)
2256 data_color(attribute_first_data(attr_node), ¨class->line_color);
2257 attr_node = object_find_attribute(obj_node, "text_color");
2258 if(attr_node != NULL)
2259 data_color(attribute_first_data(attr_node), ¨class->text_color);
2260
2261 umlclass->fill_color = color_white;
2262 /* support the old name ... */
2263 attr_node = object_find_attribute(obj_node, "background_color");
2264 if(attr_node != NULL)
2265 data_color(attribute_first_data(attr_node), ¨class->fill_color);
2266 /* ... but prefer the new one */
2267 attr_node = object_find_attribute(obj_node, "fill_color");
2268 if(attr_node != NULL)
2269 data_color(attribute_first_data(attr_node), ¨class->fill_color);
2270
2271 /* Attribute info: */
2272 list = umlclass->attributes;
2273 while (list) {
2274 UMLAttribute *attr = list->data;
2275 g_assert(attr);
2276
2277 uml_attribute_ensure_connection_points (attr, obj);
2278 list = g_list_next(list);
2279 }
2280
2281 /* Operations info: */
2282 list = umlclass->operations;
2283 while (list) {
2284 UMLOperation *op = (UMLOperation *)list->data;
2285 g_assert(op);
2286
2287 uml_operation_ensure_connection_points (op, obj);
2288 list = g_list_next(list);
2289 }
2290
2291 /* Template info: */
2292 umlclass->template = FALSE;
2293 attr_node = object_find_attribute(obj_node, "template");
2294 if (attr_node != NULL)
2295 umlclass->template = data_boolean(attribute_first_data(attr_node));
2296
2297 fill_in_fontdata(umlclass);
2298
2299 umlclass->stereotype_string = NULL;
2300
2301 umlclass_calculate_data(umlclass);
2302
2303 elem->extra_spacing.border_trans = umlclass->line_width/2.0;
2304 umlclass_update_data(umlclass);
2305
2306 for (i=0;i<8;i++) {
2307 obj->handles[i]->type = HANDLE_NON_MOVABLE;
2308 }
2309
2310 #ifdef DEBUG
2311 umlclass_sanity_check(umlclass, "Loaded class");
2312 #endif
2313
2314 return ¨class->element.object;
2315 }
2316
2317 /** Returns the number of connection points used by the attributes and
2318 * connections in the current state of the object.
2319 */
2320 static int
umlclass_num_dynamic_connectionpoints(UMLClass * umlclass)2321 umlclass_num_dynamic_connectionpoints(UMLClass *umlclass) {
2322 int num = 0;
2323 if ( (umlclass->visible_attributes) &&
2324 (!umlclass->suppress_attributes)) {
2325 GList *list = umlclass->attributes;
2326 num += 2 * g_list_length(list);
2327 }
2328
2329 if ( (umlclass->visible_operations) &&
2330 (!umlclass->suppress_operations)) {
2331 GList *list = umlclass->operations;
2332 num += 2 * g_list_length(list);
2333 }
2334 return num;
2335 }
2336
2337 void
umlclass_sanity_check(UMLClass * c,gchar * msg)2338 umlclass_sanity_check(UMLClass *c, gchar *msg)
2339 {
2340 #ifdef UML_MAINPOINT
2341 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS + 1;
2342 #else
2343 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS;
2344 #endif
2345 DiaObject *obj = (DiaObject*)c;
2346 GList *attrs;
2347 int i;
2348
2349 dia_object_sanity_check((DiaObject *)c, msg);
2350
2351 /* Check that num_connections is correct */
2352 dia_assert_true(num_fixed_connections + umlclass_num_dynamic_connectionpoints(c)
2353 == obj->num_connections,
2354 "%s: Class %p has %d connections, but %d fixed and %d dynamic\n",
2355 msg, c, obj->num_connections, num_fixed_connections,
2356 umlclass_num_dynamic_connectionpoints(c));
2357
2358 for (i = 0; i < UMLCLASS_CONNECTIONPOINTS; i++) {
2359 dia_assert_true(&c->connections[i] == obj->connections[i],
2360 "%s: Class %p connection mismatch at %d: %p != %p\n",
2361 msg, c, i, &c->connections[i], obj->connections[i]);
2362 }
2363
2364 #ifdef UML_MAINPOINT
2365 dia_assert_true(&c->connections[i] ==
2366 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2367 "%s: Class %p mainpoint mismatch: %p != %p (at %d)\n",
2368 msg, c, i, &c->connections[i],
2369 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2370 i + umlclass_num_dynamic_connectionpoints(c));
2371 #endif
2372
2373 /* Check that attributes are set up right. */
2374 i = 0;
2375 for (attrs = c->attributes; attrs != NULL; attrs = g_list_next(attrs)) {
2376 UMLAttribute *attr = (UMLAttribute *)attrs->data;
2377
2378 dia_assert_true(attr->name != NULL,
2379 "%s: %p attr %d has null name\n",
2380 msg, c, i);
2381 dia_assert_true(attr->type != NULL,
2382 "%s: %p attr %d has null type\n",
2383 msg, c, i);
2384 #if 0 /* attr->comment == NULL is fine everywhere else */
2385 dia_assert_true(attr->comment != NULL,
2386 "%s: %p attr %d has null comment\n",
2387 msg, c, i);
2388 #endif
2389
2390 /* the following checks are only right with visible attributes */
2391 if (c->visible_attributes && !c->suppress_attributes) {
2392 int conn_offset = UMLCLASS_CONNECTIONPOINTS + 2 * i;
2393
2394 dia_assert_true(attr->left_connection != NULL,
2395 "%s: %p attr %d has null left connection\n",
2396 msg, c, i);
2397 dia_assert_true(attr->right_connection != NULL,
2398 "%s: %p attr %d has null right connection\n",
2399 msg, c, i);
2400
2401 dia_assert_true(attr->left_connection == obj->connections[conn_offset],
2402 "%s: %p attr %d left conn %p doesn't match obj conn %d: %p\n",
2403 msg, c, i, attr->left_connection,
2404 conn_offset, obj->connections[conn_offset]);
2405 dia_assert_true(attr->right_connection == obj->connections[conn_offset + 1],
2406 "%s: %p attr %d right conn %p doesn't match obj conn %d: %p\n",
2407 msg, c, i, attr->right_connection,
2408 conn_offset + 1, obj->connections[conn_offset + 1]);
2409 i++;
2410 }
2411 }
2412 /* Check that operations are set up right. */
2413 }
2414
2415
2416