2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / UML / class.c
blob1f3445dc6300618dc4b9125106c33e2ca3939a81
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.
18 * File: class.c
20 * Purpose: This file contains implementation of the "class" code.
23 /** \file objects/UML/class.c Implementation of the 'UML - Class' type */
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <assert.h>
30 #include <gtk/gtk.h>
31 #include <math.h>
32 #include <string.h>
34 #include "intl.h"
35 #include "diarenderer.h"
36 #include "attributes.h"
37 #include "properties.h"
38 #include "diamenu.h"
40 #include "class.h"
42 #include "pixmaps/umlclass.xpm"
44 #include "debug.h"
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
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);
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);
70 static DiaMenu * umlclass_object_menu(DiaObject *obj, Point *p);
71 static ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data);
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);
77 static void fill_in_fontdata(UMLClass *umlclass);
78 static int umlclass_num_dynamic_connectionpoints(UMLClass *class);
80 static ObjectTypeOps umlclass_type_ops =
82 (CreateFunc) umlclass_create,
83 (LoadFunc) umlclass_load,
84 (SaveFunc) umlclass_save
87 /**
88 * This is the type descriptor for a UML - Class. It contains the
89 * information used by Dia to create an object of this type. The structure
90 * of this data type is defined in the header file object.h. When a
91 * derivation of class is required, then this type can be copied and then
92 * change the name and any other fields that are variances from the base
93 * type.
95 DiaObjectType umlclass_type =
97 "UML - Class", /* name */
98 0, /* version */
99 (char **) umlclass_xpm, /* pixmap */
101 &umlclass_type_ops, /* ops */
102 NULL,
103 (void*)0
106 /** \brief vtable for UMLClass */
107 static ObjectOps umlclass_ops = {
108 (DestroyFunc) umlclass_destroy,
109 (DrawFunc) umlclass_draw,
110 (DistanceFunc) umlclass_distance_from,
111 (SelectFunc) umlclass_select,
112 (CopyFunc) umlclass_copy,
113 (MoveFunc) umlclass_move,
114 (MoveHandleFunc) umlclass_move_handle,
115 (GetPropertiesFunc) umlclass_get_properties,
116 (ApplyPropertiesFunc) umlclass_apply_props_from_dialog,
117 (ObjectMenuFunc) umlclass_object_menu,
118 (DescribePropsFunc) umlclass_describe_props,
119 (GetPropsFunc) umlclass_get_props,
120 (SetPropsFunc) umlclass_set_props
123 extern PropDescDArrayExtra umlattribute_extra;
124 extern PropDescDArrayExtra umloperation_extra;
125 extern PropDescDArrayExtra umlparameter_extra;
126 extern PropDescDArrayExtra umlformalparameter_extra;
128 /** Properties of UMLClass */
129 static PropDescription umlclass_props[] = {
130 ELEMENT_COMMON_PROPERTIES,
131 /* 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 */
132 PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
133 PROP_STD_LINE_COLOUR_OPTIONAL,
134 PROP_STD_FILL_COLOUR_OPTIONAL,
136 PROP_STD_NOTEBOOK_BEGIN,
137 PROP_NOTEBOOK_PAGE("class", PROP_FLAG_DONT_MERGE, N_("Class")),
138 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
139 N_("Name"), NULL, NULL },
140 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
141 N_("Stereotype"), NULL, NULL },
142 { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
143 N_("Comment"), NULL, NULL },
144 { "abstract", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
145 N_("Abstract"), NULL, NULL },
146 { "template", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL| PROP_FLAG_NO_DEFAULTS,
147 N_("Template"), NULL, NULL },
149 { "suppress_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
150 N_("Suppress Attributes"), NULL, NULL },
151 { "suppress_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
152 N_("Suppress Operations"), NULL, NULL },
153 { "visible_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
154 N_("Visible Attributes"), NULL, NULL },
155 { "visible_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
156 N_("Visible Operations"), NULL, NULL },
157 { "visible_comments", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
158 N_("Visible Comments"), NULL, NULL },
159 { "wrap_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
160 N_("Wrap Operations"), NULL, NULL },
161 { "wrap_after_char", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
162 N_("Wrap after char"), NULL, NULL },
163 { "comment_line_length", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
164 N_("Comment line length"), NULL, NULL},
165 { "comment_tagging", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
166 N_("Comment tagging"), NULL, NULL},
168 /* all this just to make the defaults selectable ... */
169 PROP_NOTEBOOK_PAGE("font", PROP_FLAG_DONT_MERGE, N_("Font")),
170 PROP_STD_MULTICOL_BEGIN,
171 PROP_MULTICOL_COLUMN("font"),
172 /* FIXME: apparently multicol does not work correctly, this should be FIRST column */
173 { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
174 N_("Normal"), NULL, NULL },
175 { "polymorphic_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
176 N_("Polymorphic"), NULL, NULL },
177 { "abstract_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
178 N_("Abstract"), NULL, NULL },
179 { "classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
180 N_("Classname"), NULL, NULL },
181 { "abstract_classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
182 N_("Abstract Classname"), NULL, NULL },
183 { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
184 N_("Comment"), NULL, NULL },
186 PROP_MULTICOL_COLUMN("height"),
187 { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
188 N_(" "), NULL, NULL },
189 { "polymorphic_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
190 N_(" "), NULL, NULL },
191 { "abstract_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
192 N_(" "), NULL, NULL },
193 { "classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
194 N_(" "), NULL, NULL },
195 { "abstract_classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
196 N_(" "), NULL, NULL },
197 { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
198 N_(" "), NULL, NULL },
199 PROP_STD_MULTICOL_END,
200 PROP_STD_NOTEBOOK_END,
202 /* these are used during load, but currently not during save */
203 { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
204 N_("Attributes"), NULL, NULL /* umlattribute_extra */ },
205 { "operations", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
206 N_("Operations"), NULL, NULL /* umloperations_extra */ },
207 /* the naming is questionable, but kept for compatibility */
208 { "templates", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
209 N_("Template Parameters"), NULL, NULL /* umlformalparameters_extra */ },
211 PROP_DESC_END
214 static PropDescription *
215 umlclass_describe_props(UMLClass *umlclass)
217 if (umlclass_props[0].quark == 0) {
218 int i = 0;
220 prop_desc_list_calculate_quarks(umlclass_props);
221 while (umlclass_props[i].name != NULL) {
222 /* can't do this static, at least not on win32
223 * due to relocation (initializer not a constant)
225 if (0 == strcmp(umlclass_props[i].name, "attributes"))
226 umlclass_props[i].extra_data = &umlattribute_extra;
227 else if (0 == strcmp(umlclass_props[i].name, "operations")) {
228 PropDescription *records = umloperation_extra.common.record;
229 int j = 0;
231 umlclass_props[i].extra_data = &umloperation_extra;
232 while (records[j].name != NULL) {
233 if (0 == strcmp(records[j].name, "parameters"))
234 records[j].extra_data = &umlparameter_extra;
235 j++;
238 else if (0 == strcmp(umlclass_props[i].name, "templates"))
239 umlclass_props[i].extra_data = &umlformalparameter_extra;
241 i++;
244 return umlclass_props;
247 static PropOffset umlclass_offsets[] = {
248 ELEMENT_COMMON_PROPERTIES_OFFSETS,
250 { "text_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, text_color) },
251 { "line_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, line_color) },
252 { "fill_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, fill_color) },
253 { "name", PROP_TYPE_STRING, offsetof(UMLClass, name) },
254 { "stereotype", PROP_TYPE_STRING, offsetof(UMLClass, stereotype) },
255 { "comment", PROP_TYPE_STRING, offsetof(UMLClass, comment) },
256 { "abstract", PROP_TYPE_BOOL, offsetof(UMLClass, abstract) },
257 { "template", PROP_TYPE_BOOL, offsetof(UMLClass, template) },
258 { "suppress_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_attributes) },
259 { "visible_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , visible_attributes) },
260 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
261 { "suppress_operations", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_operations) },
262 { "visible_operations", PROP_TYPE_BOOL, offsetof(UMLClass , visible_operations) },
263 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
264 { "wrap_operations", PROP_TYPE_BOOL, offsetof(UMLClass , wrap_operations) },
265 { "wrap_after_char", PROP_TYPE_INT, offsetof(UMLClass , wrap_after_char) },
266 { "comment_line_length", PROP_TYPE_INT, offsetof(UMLClass, comment_line_length) },
267 { "comment_tagging", PROP_TYPE_BOOL, offsetof(UMLClass, comment_tagging) },
269 /* all this just to make the defaults selectable ... */
270 PROP_OFFSET_STD_MULTICOL_BEGIN,
271 PROP_OFFSET_MULTICOL_COLUMN("font"),
272 { "normal_font", PROP_TYPE_FONT, offsetof(UMLClass, normal_font) },
273 { "abstract_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_font) },
274 { "polymorphic_font", PROP_TYPE_FONT, offsetof(UMLClass, polymorphic_font) },
275 { "classname_font", PROP_TYPE_FONT, offsetof(UMLClass, classname_font) },
276 { "abstract_classname_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_classname_font) },
277 { "comment_font", PROP_TYPE_FONT, offsetof(UMLClass, comment_font) },
279 PROP_OFFSET_MULTICOL_COLUMN("height"),
280 { "normal_font_height", PROP_TYPE_REAL, offsetof(UMLClass, font_height) },
281 { "abstract_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_font_height) },
282 { "polymorphic_font_height", PROP_TYPE_REAL, offsetof(UMLClass, polymorphic_font_height) },
283 { "classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, classname_font_height) },
284 { "abstract_classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_classname_font_height) },
285 { "comment_font_height", PROP_TYPE_REAL, offsetof(UMLClass, comment_font_height) },
286 PROP_OFFSET_STD_MULTICOL_END,
288 { "operations", PROP_TYPE_DARRAY, offsetof(UMLClass , operations) },
289 { "attributes", PROP_TYPE_DARRAY, offsetof(UMLClass , attributes) } ,
290 { "templates", PROP_TYPE_DARRAY, offsetof(UMLClass , formal_params) } ,
292 { NULL, 0, 0 },
295 static void
296 umlclass_get_props(UMLClass * umlclass, GPtrArray *props)
298 object_get_props_from_offsets(&umlclass->element.object,
299 umlclass_offsets, props);
302 static DiaMenuItem umlclass_menu_items[] = {
303 { N_("Show Comments"), umlclass_show_comments_callback, NULL,
304 DIAMENU_ACTIVE|DIAMENU_TOGGLE },
307 static DiaMenu umlclass_menu = {
308 N_("Class"),
309 sizeof(umlclass_menu_items)/sizeof(DiaMenuItem),
310 umlclass_menu_items,
311 NULL
314 DiaMenu *
315 umlclass_object_menu(DiaObject *obj, Point *p)
317 umlclass_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
318 (((UMLClass *)obj)->visible_comments?DIAMENU_TOGGLE_ON:0);
320 return &umlclass_menu;
323 ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data)
325 ObjectChange *change = new_object_state_change(obj, NULL, NULL, NULL );
327 ((UMLClass *)obj)->visible_comments = !((UMLClass *)obj)->visible_comments;
328 umlclass_calculate_data((UMLClass *)obj);
329 umlclass_update_data((UMLClass *)obj);
330 return change;
333 static void
334 umlclass_set_props(UMLClass *umlclass, GPtrArray *props)
336 /* now that operations/attributes can be set here as well we need to
337 * take for the number of connections update as well
338 * Note that due to a hack in umlclass_load, this is called before
339 * the normal connection points are set up.
341 DiaObject *obj = &umlclass->element.object;
342 GList *list;
343 int num;
345 object_set_props_from_offsets(&umlclass->element.object, umlclass_offsets,
346 props);
348 num = UMLCLASS_CONNECTIONPOINTS + umlclass_num_dynamic_connectionpoints(umlclass);
350 #ifdef UML_MAINPOINT
351 obj->num_connections = num + 1;
352 #else
353 obj->num_connections = num;
354 #endif
356 obj->connections = g_realloc(obj->connections, obj->num_connections*sizeof(ConnectionPoint *));
358 /* Update data: */
359 if (num > UMLCLASS_CONNECTIONPOINTS) {
360 int i;
361 /* this is just updating pointers to ConnectionPoint, the real connection handling is elsewhere.
362 * Note: Can't optimize here on number change cause the ops/attribs may have changed regardless of that.
364 i = UMLCLASS_CONNECTIONPOINTS;
365 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
366 while (list != NULL) {
367 UMLAttribute *attr = (UMLAttribute *)list->data;
369 uml_attribute_ensure_connection_points (attr, obj);
370 obj->connections[i] = attr->left_connection;
371 obj->connections[i]->object = obj;
372 i++;
373 obj->connections[i] = attr->right_connection;
374 obj->connections[i]->object = obj;
375 i++;
376 list = g_list_next(list);
378 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
379 while (list != NULL) {
380 UMLOperation *op = (UMLOperation *)list->data;
382 uml_operation_ensure_connection_points (op, obj);
383 obj->connections[i] = op->left_connection;
384 obj->connections[i]->object = obj;
385 i++;
386 obj->connections[i] = op->right_connection;
387 obj->connections[i]->object = obj;
388 i++;
389 list = g_list_next(list);
392 #ifdef UML_MAINPOINT
393 obj->connections[num] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
394 obj->connections[num]->object = obj;
395 #endif
397 umlclass_calculate_data(umlclass);
398 umlclass_update_data(umlclass);
399 /* Would like to sanity check here, but the call to object_load_props
400 * in umlclass_load means we will be called with inconsistent data. */
401 umlclass_sanity_check(umlclass, "After updating data");
404 static real
405 umlclass_distance_from(UMLClass *umlclass, Point *point)
407 DiaObject *obj = &umlclass->element.object;
408 return distance_rectangle_point(&obj->bounding_box, point);
411 static void
412 umlclass_select(UMLClass *umlclass, Point *clicked_point,
413 DiaRenderer *interactive_renderer)
415 element_update_handles(&umlclass->element);
418 static ObjectChange*
419 umlclass_move_handle(UMLClass *umlclass, Handle *handle,
420 Point *to, ConnectionPoint *cp,
421 HandleMoveReason reason, ModifierKeys modifiers)
423 assert(umlclass!=NULL);
424 assert(handle!=NULL);
425 assert(to!=NULL);
427 assert(handle->id < UMLCLASS_CONNECTIONPOINTS);
429 return NULL;
432 static ObjectChange*
433 umlclass_move(UMLClass *umlclass, Point *to)
435 umlclass->element.corner = *to;
436 umlclass_update_data(umlclass);
438 return NULL;
441 * underlines the text at the start point using the text to determine
442 * the length of the underline. Draw a line under the text represented by
443 * string using the selected renderer, color, and line width. Since
444 * drawing this line will change the line width used by DIA, the current
445 * line width that DIA is using is also passed so it can be restored once
446 * the line has been drawn.
448 * @param renderer the renderer that will draw the line
449 * @param StartPoint the start of the line to be drawn
450 * @param font the font used to draw the text being underlined
451 * @param font_height the size in the y direction of the font used to draw the text
452 * @param string the text string that is to be underlined
453 * @param color the color of the line to draw
454 * @param line_width default line thickness
455 * @param underline_width the thickness of the line to draw
458 static void
459 uml_underline_text(DiaRenderer *renderer,
460 Point StartPoint,
461 DiaFont *font,
462 real font_height,
463 gchar *string,
464 Color *color,
465 real line_width,
466 real underline_width)
468 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
469 Point UnderlineStartPoint;
470 Point UnderlineEndPoint;
472 UnderlineStartPoint = StartPoint;
473 UnderlineStartPoint.y += font_height * 0.1;
474 UnderlineEndPoint = UnderlineStartPoint;
475 UnderlineEndPoint.x += dia_font_string_width(string, font, font_height);
476 renderer_ops->set_linewidth(renderer, underline_width);
477 renderer_ops->draw_line(renderer, &UnderlineStartPoint, &UnderlineEndPoint, color);
478 renderer_ops->set_linewidth(renderer, line_width);
482 * Create a documentation tag from a comment.
484 * First a string is created containing only the text
485 * "{documentation = ". Then the contents of the comment string
486 * are added but wrapped. This is done by first looking for any
487 * New Line characters. If the line segment is longer than the
488 * WrapPoint would allow, the line is broken at either the
489 * first whitespace before the WrapPoint or if there are no
490 * whitespaces in the segment, at the WrapPoint. This
491 * continues until the entire string has been processed and
492 * then the resulting new string is returned. No attempt is
493 * made to rejoin any of the segments, that is all New Lines
494 * are treated as hard newlines. No syllable matching is done
495 * either so breaks in words will sometimes not make real
496 * sense.
497 * <p>
498 * Finally, since this function returns newly created dynamic
499 * memory the caller must free the memory to prevent memory
500 * leaks.
502 * @param comment The comment to be wrapped to the line length limit
503 * @param WrapPoint The maximum line length allowed for the line.
504 * @param NumberOfLines The number of comment lines after the wrapping.
505 * @return a pointer to the wrapped documentation
507 * NOTE:
508 * This function should most likely be move to a source file for
509 * handling global UML functionallity at some point.
511 static gchar *
512 uml_create_documentation_tag (gchar * comment,
513 gboolean tagging,
514 gint WrapPoint,
515 gint *NumberOfLines)
517 gchar *CommentTag = tagging ? "{documentation = " : "";
518 gint TagLength = strlen(CommentTag);
519 /* Make sure that there is at least some value greater then zero for the WrapPoint to
520 * support diagrams from earlier versions of Dia. So if the WrapPoint is zero then use
521 * the taglength as the WrapPoint. If the Tag has been changed such that it has a length
522 * of 0 then use 1.
524 gint WorkingWrapPoint = (TagLength<WrapPoint) ? WrapPoint : ((TagLength<=0)?1:TagLength);
525 gint RawLength = TagLength + strlen(comment) + (tagging?1:0);
526 gint MaxCookedLength = RawLength + RawLength/WorkingWrapPoint;
527 gchar *WrappedComment = g_malloc0(MaxCookedLength+1);
528 gint AvailSpace = WorkingWrapPoint - TagLength;
529 gchar *Scan;
530 gchar *BreakCandidate;
531 gunichar ScanChar;
532 gboolean AddNL = FALSE;
534 if (tagging)
535 strcat(WrappedComment, CommentTag);
536 *NumberOfLines = 1;
538 while ( *comment ) {
539 /* Skip spaces */
540 while ( *comment && g_unichar_isspace(g_utf8_get_char(comment)) ) {
541 comment = g_utf8_next_char(comment);
543 /* Copy chars */
544 if ( *comment ){
545 /* Scan to \n or avalable space exhausted */
546 Scan = comment;
547 BreakCandidate = NULL;
548 while ( *Scan && *Scan != '\n' && AvailSpace > 0 ) {
549 ScanChar = g_utf8_get_char(Scan);
550 /* We known, that g_unichar_isspace() is not recommended for word breaking;
551 * but Pango usage seems too complex.
553 if ( g_unichar_isspace(ScanChar) )
554 BreakCandidate = Scan;
555 AvailSpace--; /* not valid for nonspacing marks */
556 Scan = g_utf8_next_char(Scan);
558 if ( AvailSpace==0 && BreakCandidate != NULL )
559 Scan = BreakCandidate;
560 if ( AddNL ){
561 strcat(WrappedComment, "\n");
562 *NumberOfLines+=1;
564 AddNL = TRUE;
565 strncat(WrappedComment, comment, Scan-comment);
566 AvailSpace = WorkingWrapPoint;
567 comment = Scan;
570 if (tagging)
571 strcat(WrappedComment, "}");
572 assert(strlen(WrappedComment)<=MaxCookedLength);
573 return WrappedComment;
577 * Draw the comment at the point, p, using the comment font from the
578 * class defined by umlclass. When complete update the point to reflect
579 * the size of data drawn.
580 * The comment will have been word wrapped using the function
581 * uml_create_documentation_tag, so it may have more than one line on the
582 * display.
584 * @param renderer The Renderer on which the comment is being drawn
585 * @param font The font to render the comment in.
586 * @param font_height The Y size of the font used to render the comment
587 * @param text_color A pointer to the color to use to render the comment
588 * @param comment The comment string to render
589 * @param comment_tagging If the {documentation = } tag should be enforced
590 * @param Comment_line_length The maximum length of any one line in the comment
591 * @param p The point at which the comment is to start
592 * @param alignment The method to use for alignment of the font
593 * @see uml_create_documentation
595 static void
596 uml_draw_comments(DiaRenderer *renderer,
597 DiaFont *font,
598 real font_height,
599 Color *text_color,
600 gchar *comment,
601 gboolean comment_tagging,
602 gint Comment_line_length,
603 Point *p,
604 gint alignment)
606 gint NumberOfLines = 0;
607 gint Index;
608 gchar *CommentString = 0;
609 gchar *NewLineP= NULL;
610 gchar *RenderP;
612 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
614 CommentString =
615 uml_create_documentation_tag(comment, comment_tagging, Comment_line_length, &NumberOfLines);
616 RenderP = CommentString;
617 renderer_ops->set_font(renderer, font, font_height);
618 for ( Index=0; Index < NumberOfLines; Index++)
620 p->y += font_height; /* Advance to the next line */
621 NewLineP = strchr(RenderP, '\n');
622 if ( NewLineP != NULL)
624 *NewLineP++ = '\0';
626 renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
627 RenderP = NewLineP;
628 if ( NewLineP == NULL){
629 break;
632 g_free(CommentString);
637 * Draw the name box of the class icon. According to the UML specification,
638 * the Name box or compartment is the top most compartment of the class
639 * icon. It may contain one or more stereotype declarations, followed by
640 * the name of the class. The name may be rendered to indicate abstraction
641 * for abstract classes. Following the name is any tagged values such as
642 * the {documentation = } tag.
643 * <p>
644 * Because the start point is the upper left of the class box, templates
645 * tend to get lost when created. By applying an offset, they will not be
646 * lost. The offset should only be added if the elem->corner.y = 0.
648 * @param umlclass The pointer to the class being drawn
649 * @param renderer The pointer to the rendering object used to draw
650 * @param elem The pointer to the element within the class to be drawn
651 * @param offset offset from start point
652 * @return The offset from the start of the class to the bottom of the namebox
655 static real
656 umlclass_draw_namebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem )
658 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
659 real font_height;
660 DiaFont *font;
661 Point StartPoint;
662 Point LowerRightPoint;
663 real Yoffset;
664 Color *text_color = &umlclass->text_color;
668 StartPoint.x = elem->corner.x;
669 StartPoint.y = elem->corner.y;
671 Yoffset = elem->corner.y + umlclass->namebox_height;
673 LowerRightPoint = StartPoint;
674 LowerRightPoint.x += elem->width;
675 LowerRightPoint.y = Yoffset;
678 * First draw the outer box and fill color for the class name
679 * object
681 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->fill_color);
682 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->line_color);
684 /* Start at the midpoint on the X axis */
685 StartPoint.x += elem->width / 2.0;
687 /* stereotype: */
688 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
689 gchar *String = umlclass->stereotype_string;
690 StartPoint.y += 0.1;
691 StartPoint.y += dia_font_ascent(String, umlclass->normal_font, umlclass->font_height);
692 renderer_ops->set_font(renderer, umlclass->normal_font, umlclass->font_height);
693 renderer_ops->draw_string(renderer, String, &StartPoint, ALIGN_CENTER, text_color);
696 /* name: */
697 if (umlclass->name != NULL) {
698 if (umlclass->abstract) {
699 font = umlclass->abstract_classname_font;
700 font_height = umlclass->abstract_classname_font_height;
701 } else {
702 font = umlclass->classname_font;
703 font_height = umlclass->classname_font_height;
705 StartPoint.y += font_height;
707 renderer_ops->set_font(renderer, font, font_height);
708 renderer_ops->draw_string(renderer, umlclass->name, &StartPoint, ALIGN_CENTER, text_color);
711 /* comment */
712 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0'){
713 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
714 &umlclass->text_color, umlclass->comment, umlclass->comment_tagging,
715 umlclass->comment_line_length, &StartPoint, ALIGN_CENTER);
717 return Yoffset;
720 /**
721 * Draw the attribute box.
722 * This attribute box follows the name box in the class icon. If the
723 * attributes are not suppress, draw each of the attributes following the
724 * UML specification for displaying attributes. Each attribute is preceded
725 * by the visibility character, +, - or # depending on whether it is public
726 * private or protected. If the attribute is "abstract" it will be rendered
727 * using the abstract font otherwise it will be rendered using the normal
728 * font. If the attribute is of class scope, static in C++, then it will be
729 * underlined. If there is a comment associated with the attribute, that is
730 * within the class description, it will be rendered as a uml comment.
732 * @param umlclass The pointer to the class being drawn
733 * @param renderer The pointer to the rendering object used to draw
734 * @param elem The pointer to the element within the class to be drawn
735 * @param Yoffset The Y offset from the start of the class at which to draw the attributebox
736 * @return The offset from the start of the class to the bottom of the attributebox
737 * @see uml_draw_comments
739 static real
740 umlclass_draw_attributebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
742 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
743 real font_height;
744 Point StartPoint;
745 Point LowerRight;
746 DiaFont *font;
747 Color *fill_color = &umlclass->fill_color;
748 Color *line_color = &umlclass->line_color;
749 Color *text_color = &umlclass->text_color;
750 GList *list;
752 StartPoint.x = elem->corner.x;
753 StartPoint.y = Yoffset;
754 Yoffset += umlclass->attributesbox_height;
756 LowerRight = StartPoint;
757 LowerRight.x += elem->width;
758 LowerRight.y = Yoffset;
760 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
761 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
763 if (!umlclass->suppress_attributes) {
764 gint i = 0;
765 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
766 StartPoint.y += 0.1;
768 list = umlclass->attributes;
769 while (list != NULL)
771 UMLAttribute *attr = (UMLAttribute *)list->data;
772 gchar *attstr = uml_get_attribute_string(attr);
774 if (attr->abstract) {
775 font = umlclass->abstract_font;
776 font_height = umlclass->abstract_font_height;
778 else {
779 font = umlclass->normal_font;
780 font_height = umlclass->font_height;
782 StartPoint.y += font_height;
783 renderer_ops->set_font (renderer, font, font_height);
784 renderer_ops->draw_string(renderer, attstr, &StartPoint, ALIGN_LEFT, text_color);
786 if (attr->class_scope) {
787 uml_underline_text(renderer, StartPoint, font, font_height, attstr, line_color,
788 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
791 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
792 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
793 &umlclass->text_color, attr->comment, umlclass->comment_tagging,
794 umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
795 StartPoint.y += umlclass->comment_font_height/2;
797 list = g_list_next(list);
798 i++;
799 g_free (attstr);
802 return Yoffset;
807 * Draw the operations box. The operations block follows the attribute box
808 * if it is visible. If the operations are not suppressed, they are
809 * displayed in the operations box. Like the attributes, operations have
810 * visibility characters, +,-, and # indicating whether the are public,
811 * private or protected. The operations are rendered in different fonts
812 * depending on whether they are abstract (pure virtual), polymorphic
813 * (virtual) or leaf (final virtual or non-virtual). The parameters to the
814 * operation may be displayed and if they are they may be conditionally
815 * wrapped to reduce horizontial size of the icon.
817 * @param umlclass The pointer to the class being drawn
818 * @param renderer The pointer to the rendering object used to draw
819 * @param elem The pointer to the element within the class to be drawn
820 * @param Yoffset The Y offset from the start of the class at which to draw the operationbox
821 * @return The offset from the start of the class to the bottom of the operationbox
824 static real
825 umlclass_draw_operationbox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
827 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
828 real font_height;
829 Point StartPoint;
830 Point LowerRight;
831 DiaFont *font;
832 GList *list;
833 Color *fill_color = &umlclass->fill_color;
834 Color *line_color = &umlclass->line_color;
835 Color *text_color = &umlclass->text_color;
838 StartPoint.x = elem->corner.x;
839 StartPoint.y = Yoffset;
840 Yoffset += umlclass->operationsbox_height;
842 LowerRight = StartPoint;
843 LowerRight.x += elem->width;
844 LowerRight.y = Yoffset;
846 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
847 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
849 if (!umlclass->suppress_operations) {
850 gint i = 0;
851 GList *wrapsublist = NULL;
852 gchar *part_opstr = NULL;
853 int wrap_pos, last_wrap_pos, ident, wrapping_needed;
854 int part_opstr_len = 0, part_opstr_need = 0;
856 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
857 StartPoint.y += 0.1;
859 list = umlclass->operations;
860 while (list != NULL) {
861 UMLOperation *op = (UMLOperation *)list->data;
862 gchar* opstr = uml_get_operation_string(op);
863 real ascent;
865 switch (op->inheritance_type) {
866 case UML_ABSTRACT:
867 font = umlclass->abstract_font;
868 font_height = umlclass->abstract_font_height;
869 break;
870 case UML_POLYMORPHIC:
871 font = umlclass->polymorphic_font;
872 font_height = umlclass->polymorphic_font_height;
873 break;
874 case UML_LEAF:
875 default:
876 font = umlclass->normal_font;
877 font_height = umlclass->font_height;
880 wrapping_needed = 0;
881 if( umlclass->wrap_operations ) {
882 wrapsublist = op->wrappos;
885 ascent = dia_font_ascent(opstr, font, font_height);
886 op->ascent = ascent;
887 renderer_ops->set_font(renderer, font, font_height);
889 if( umlclass->wrap_operations && op->needs_wrapping) {
890 ident = op->wrap_indent;
891 wrapsublist = op->wrappos;
892 wrap_pos = last_wrap_pos = 0;
894 while( wrapsublist != NULL) {
895 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
897 if( last_wrap_pos == 0) {
898 part_opstr_need = wrap_pos + 1;
899 if (part_opstr_len < part_opstr_need) {
900 part_opstr_len = part_opstr_need;
901 part_opstr = g_realloc (part_opstr, part_opstr_need);
903 strncpy( part_opstr, opstr, wrap_pos);
904 memset( part_opstr+wrap_pos, '\0', 1);
906 else {
907 part_opstr_need = ident + wrap_pos - last_wrap_pos + 1;
908 if (part_opstr_len < part_opstr_need) {
909 part_opstr_len = part_opstr_need;
910 part_opstr = g_realloc (part_opstr, part_opstr_need);
912 memset( part_opstr, ' ', ident);
913 memset( part_opstr+ident, '\0', 1);
914 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
917 StartPoint.y += ascent;
918 renderer_ops->draw_string(renderer, part_opstr, &StartPoint, ALIGN_LEFT, text_color);
919 last_wrap_pos = wrap_pos;
920 wrapsublist = g_list_next( wrapsublist);
923 else
925 StartPoint.y += ascent;
926 renderer_ops->draw_string(renderer, opstr, &StartPoint, ALIGN_LEFT, text_color);
929 if (op->class_scope) {
930 uml_underline_text(renderer, StartPoint, font, font_height, opstr, line_color,
931 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
934 StartPoint.y += font_height - ascent;
936 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
937 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
938 &umlclass->text_color, op->comment, umlclass->comment_tagging,
939 umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
942 list = g_list_next(list);
943 i++;
944 g_free (opstr);
946 if (part_opstr){
947 g_free(part_opstr);
950 return Yoffset;
954 * Draw the template parameters box in the upper right hand corner of the
955 * class box for paramertize classes (aka template classes). Fill in this
956 * box with the parameters for the class.
957 * <p>
958 * At this time there is no provision for adding comments or documentation
959 * to the display.
961 * @param umlclass The pointer to the class being drawn
962 * @param renderer The pointer to the rendering object used to draw
963 * @param elem The pointer to the element within the class to be drawn
966 static void
967 umlclass_draw_template_parameters_box(UMLClass *umlclass, DiaRenderer *renderer, Element *elem)
969 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
970 Point UpperLeft;
971 Point LowerRight;
972 Point TextInsert;
973 GList *list;
974 gint i;
975 DiaFont *font = umlclass->normal_font;
976 real font_height = umlclass->font_height;
977 Color *fill_color = &umlclass->fill_color;
978 Color *line_color = &umlclass->line_color;
979 Color *text_color = &umlclass->text_color;
983 * Adjust for the overlay of the template on the class icon
985 UpperLeft.x = elem->corner.x + elem->width - UMLCLASS_TEMPLATE_OVERLAY_X;
986 UpperLeft.y = elem->corner.y - umlclass->templates_height + UMLCLASS_TEMPLATE_OVERLAY_Y;
987 TextInsert = UpperLeft;
988 LowerRight = UpperLeft;
989 LowerRight.x += umlclass->templates_width;
990 LowerRight.y += umlclass->templates_height;
992 renderer_ops->fill_rect(renderer, &UpperLeft, &LowerRight, fill_color);
993 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
994 renderer_ops->set_dashlength(renderer, 0.3);
995 renderer_ops->draw_rect(renderer, &UpperLeft, &LowerRight, line_color);
997 TextInsert.x += 0.3;
998 renderer_ops->set_font(renderer, font, font_height);
999 i = 0;
1000 list = umlclass->formal_params;
1001 while (list != NULL)
1003 gchar *paramstr = uml_get_formalparameter_string((UMLFormalParameter *)list->data);
1005 TextInsert.y +=(0.1 + dia_font_ascent(paramstr, font, font_height));
1006 renderer_ops->draw_string(renderer, paramstr, &TextInsert, ALIGN_LEFT, text_color);
1008 list = g_list_next(list);
1009 i++;
1010 g_free (paramstr);
1015 * Draw the class icon for the specified UMLClass object.
1016 * Set the renderer to the correct fill and line styles and the appropriate
1017 * line width. The object is drawn by the umlclass_draw_namebox,
1018 * umlclass_draw_attributebox, umlclass_draw_operationbox and
1019 * umlclass_draw_template_parameters_box.
1021 * @param umlclass object based on the uml class that is being rendered
1022 * @param DiaRenderer renderer used to draw the object
1026 static void
1027 umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer)
1029 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1030 real y = 0.0;
1031 Element *elem;
1033 assert(umlclass != NULL);
1034 assert(renderer != NULL);
1036 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
1037 renderer_ops->set_linewidth(renderer, UMLCLASS_BORDER);
1038 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
1040 elem = &umlclass->element;
1042 y = umlclass_draw_namebox(umlclass, renderer, elem);
1043 if (umlclass->visible_attributes) {
1044 y = umlclass_draw_attributebox(umlclass, renderer, elem, y);
1046 if (umlclass->visible_operations) {
1047 y = umlclass_draw_operationbox(umlclass, renderer, elem, y);
1049 if (umlclass->template) {
1050 umlclass_draw_template_parameters_box(umlclass, renderer, elem);
1054 void
1055 umlclass_update_data(UMLClass *umlclass)
1057 Element *elem = &umlclass->element;
1058 DiaObject *obj = &elem->object;
1059 real x,y;
1060 GList *list;
1061 int i;
1062 int pointswide;
1063 int lowerleftcorner;
1064 real pointspacing;
1066 x = elem->corner.x;
1067 y = elem->corner.y;
1069 /* Update connections: */
1070 umlclass->connections[0].pos = elem->corner;
1071 umlclass->connections[0].directions = DIR_NORTH|DIR_WEST;
1073 /* there are four corner points and two side points, thus all
1074 * remaining points are on the top/bottom width
1076 pointswide = (UMLCLASS_CONNECTIONPOINTS - 6) / 2;
1077 pointspacing = elem->width / (pointswide + 1.0);
1079 /* across the top connection points */
1080 for (i=1;i<=pointswide;i++) {
1081 umlclass->connections[i].pos.x = x + (pointspacing * i);
1082 umlclass->connections[i].pos.y = y;
1083 umlclass->connections[i].directions = DIR_NORTH;
1086 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 2;
1087 umlclass->connections[i].pos.x = x + elem->width;
1088 umlclass->connections[i].pos.y = y;
1089 umlclass->connections[i].directions = DIR_NORTH|DIR_EAST;
1091 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 1;
1092 umlclass->connections[i].pos.x = x;
1093 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1094 umlclass->connections[i].directions = DIR_WEST;
1096 i = (UMLCLASS_CONNECTIONPOINTS / 2);
1097 umlclass->connections[i].pos.x = x + elem->width;
1098 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1099 umlclass->connections[i].directions = DIR_EAST;
1101 i = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1102 umlclass->connections[i].pos.x = x;
1103 umlclass->connections[i].pos.y = y + elem->height;
1104 umlclass->connections[i].directions = DIR_WEST|DIR_SOUTH;
1106 /* across the bottom connection points */
1107 lowerleftcorner = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1108 for (i=1;i<=pointswide;i++) {
1109 umlclass->connections[lowerleftcorner + i].pos.x = x + (pointspacing * i);
1110 umlclass->connections[lowerleftcorner + i].pos.y = y + elem->height;
1111 umlclass->connections[lowerleftcorner + i].directions = DIR_SOUTH;
1114 /* bottom-right corner */
1115 i = (UMLCLASS_CONNECTIONPOINTS) - 1;
1116 umlclass->connections[i].pos.x = x + elem->width;
1117 umlclass->connections[i].pos.y = y + elem->height;
1118 umlclass->connections[i].directions = DIR_EAST|DIR_SOUTH;
1120 #ifdef UML_MAINPOINT
1121 /* Main point -- lives just after fixed connpoints in umlclass array */
1122 i = UMLCLASS_CONNECTIONPOINTS;
1123 umlclass->connections[i].pos.x = x + elem->width / 2;
1124 umlclass->connections[i].pos.y = y + elem->height / 2;
1125 umlclass->connections[i].directions = DIR_ALL;
1126 umlclass->connections[i].flags = CP_FLAGS_MAIN;
1127 #endif
1129 y += umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1131 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
1132 while (list != NULL) {
1133 UMLAttribute *attr = (UMLAttribute *)list->data;
1135 attr->left_connection->pos.x = x;
1136 attr->left_connection->pos.y = y;
1137 attr->left_connection->directions = DIR_WEST;
1138 attr->right_connection->pos.x = x + elem->width;
1139 attr->right_connection->pos.y = y;
1140 attr->right_connection->directions = DIR_EAST;
1142 y += umlclass->font_height;
1143 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1144 y += umlclass->comment_font_height;
1146 list = g_list_next(list);
1149 y = elem->corner.y + umlclass->namebox_height +
1150 umlclass->attributesbox_height + 0.1 + umlclass->font_height/2;
1152 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
1153 while (list != NULL) {
1154 UMLOperation *op = (UMLOperation *)list->data;
1156 op->left_connection->pos.x = x;
1157 op->left_connection->pos.y = y;
1158 op->left_connection->directions = DIR_WEST;
1159 op->right_connection->pos.x = x + elem->width;
1160 op->right_connection->pos.y = y;
1161 op->right_connection->directions = DIR_EAST;
1163 if (op->needs_wrapping) { /* Wrapped */
1164 int lines = g_list_length(op->wrappos);
1165 y += umlclass->font_height - op->ascent;
1166 y += op->ascent * lines;
1167 } else {
1168 y += umlclass->font_height;
1170 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0')
1171 y += umlclass->comment_font_height; /* Not adjusted for wrap */
1172 list = g_list_next(list);
1175 element_update_boundingbox(elem);
1177 if (umlclass->template) {
1178 /* fix boundingumlclass for templates: */
1179 obj->bounding_box.top -= (umlclass->templates_height - 0.3) ;
1180 obj->bounding_box.right += (umlclass->templates_width - 2.3);
1183 obj->position = elem->corner;
1185 element_update_handles(elem);
1187 umlclass_sanity_check(umlclass, "After updating data");
1191 * Calculate the dimensions of the class icons namebox for a given object of UMLClass.
1192 * The height is stored in the class structure. When calculating the
1193 * comment, if any, the comment is word wrapped and the resulting number of
1194 * lines is then used to calculate the height of the bounding box.
1196 * @param umlclass pointer to the object of UMLClass to calculate
1197 * @return the horizontal size of the name box.
1201 static real
1202 umlclass_calculate_name_data(UMLClass *umlclass)
1204 real maxwidth = 0.0;
1205 real width = 0.0;
1206 /* name box: */
1208 if (umlclass->name != NULL && umlclass->name[0] != '\0') {
1209 if (umlclass->abstract) {
1210 maxwidth = dia_font_string_width(umlclass->name,
1211 umlclass->abstract_classname_font,
1212 umlclass->abstract_classname_font_height);
1213 } else {
1214 maxwidth = dia_font_string_width(umlclass->name,
1215 umlclass->classname_font,
1216 umlclass->classname_font_height);
1220 umlclass->namebox_height = umlclass->classname_font_height + 4*0.1;
1221 if (umlclass->stereotype_string != NULL) {
1222 g_free(umlclass->stereotype_string);
1224 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
1225 umlclass->namebox_height += umlclass->font_height;
1226 umlclass->stereotype_string = g_strconcat ( UML_STEREOTYPE_START,
1227 umlclass->stereotype,
1228 UML_STEREOTYPE_END,
1229 NULL);
1231 width = dia_font_string_width (umlclass->stereotype_string,
1232 umlclass->normal_font,
1233 umlclass->font_height);
1234 maxwidth = MAX(width, maxwidth);
1235 } else {
1236 umlclass->stereotype_string = NULL;
1239 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0')
1241 int NumberOfCommentLines = 0;
1242 gchar *wrapped_box = uml_create_documentation_tag (umlclass->comment,
1243 umlclass->comment_tagging,
1244 umlclass->comment_line_length,
1245 &NumberOfCommentLines);
1247 width = dia_font_string_width (wrapped_box,
1248 umlclass->comment_font,
1249 umlclass->comment_font_height);
1251 g_free(wrapped_box);
1252 umlclass->namebox_height += umlclass->comment_font_height * NumberOfCommentLines;
1253 maxwidth = MAX(width, maxwidth);
1255 return maxwidth;
1258 * Calculate the dimensions of the attribute box on an object of type UMLClass.
1259 * @param umlclass a pointer to an object of UMLClass
1260 * @return the horizontal size of the attribute box
1264 static real
1265 umlclass_calculate_attribute_data(UMLClass *umlclass)
1267 int i;
1268 real maxwidth = 0.0;
1269 real width = 0.0;
1270 GList *list;
1272 umlclass->attributesbox_height = 2*0.1;
1274 if (g_list_length(umlclass->attributes) != 0)
1276 i = 0;
1277 list = umlclass->attributes;
1278 while (list != NULL)
1280 UMLAttribute *attr = (UMLAttribute *) list->data;
1281 gchar *attstr = uml_get_attribute_string(attr);
1283 if (attr->abstract)
1285 width = dia_font_string_width(attstr,
1286 umlclass->abstract_font,
1287 umlclass->abstract_font_height);
1288 umlclass->attributesbox_height += umlclass->abstract_font_height;
1290 else
1292 width = dia_font_string_width(attstr,
1293 umlclass->normal_font,
1294 umlclass->font_height);
1295 umlclass->attributesbox_height += umlclass->font_height;
1297 maxwidth = MAX(width, maxwidth);
1299 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1301 int NumberOfLines = 0;
1302 gchar *Wrapped = uml_create_documentation_tag(attr->comment,
1303 umlclass->comment_tagging,
1304 umlclass->comment_line_length,
1305 &NumberOfLines);
1307 width = dia_font_string_width(Wrapped,
1308 umlclass->comment_font,
1309 umlclass->comment_font_height);
1311 g_free(Wrapped);
1312 umlclass->attributesbox_height += (umlclass->comment_font_height * (NumberOfLines));
1313 umlclass->attributesbox_height += umlclass->comment_font_height/2;
1315 maxwidth = MAX(width, maxwidth);
1318 i++;
1319 list = g_list_next(list);
1320 g_free (attstr);
1324 if ((umlclass->attributesbox_height<0.4)|| umlclass->suppress_attributes )
1326 umlclass->attributesbox_height = 0.4;
1328 return maxwidth;
1332 * Calculate the dimensions of the operations box of an object of UMLClass.
1333 * The vertical size or height is stored in the object.
1334 * @param umlclass a pointer to an object of UMLClass
1335 * @return the horizontial size of the operations box
1339 static real
1340 umlclass_calculate_operation_data(UMLClass *umlclass)
1342 int i;
1343 int pos_next_comma;
1344 int pos_brace;
1345 int wrap_pos;
1346 int last_wrap_pos;
1347 int indent;
1348 int offset;
1349 int maxlinewidth;
1350 int length;
1351 real maxwidth = 0.0;
1352 real width = 0.0;
1353 GList *list;
1354 GList *wrapsublist;
1356 /* operations box: */
1357 umlclass->operationsbox_height = 2*0.1;
1359 if (0 != g_list_length(umlclass->operations))
1361 i = 0;
1362 list = umlclass->operations;
1363 while (list != NULL)
1365 UMLOperation *op = (UMLOperation *) list->data;
1366 gchar *opstr = uml_get_operation_string(op);
1367 DiaFont *Font;
1368 real FontHeight;
1370 length = strlen( (const gchar*)opstr);
1372 if (op->wrappos != NULL) {
1373 g_list_free(op->wrappos);
1375 op->wrappos = NULL;
1377 switch(op->inheritance_type)
1379 case UML_ABSTRACT:
1380 Font = umlclass->abstract_font;
1381 FontHeight = umlclass->abstract_font_height;
1382 break;
1383 case UML_POLYMORPHIC:
1384 Font = umlclass->polymorphic_font;
1385 FontHeight = umlclass->polymorphic_font_height;
1386 break;
1387 case UML_LEAF:
1388 default:
1389 Font = umlclass->normal_font;
1390 FontHeight = umlclass->font_height;
1392 op->ascent = dia_font_ascent(opstr, Font, FontHeight);
1394 if( umlclass->wrap_operations )
1396 if( length > umlclass->wrap_after_char)
1398 gchar *part_opstr;
1399 op->needs_wrapping = TRUE;
1401 /* count maximal line width to create a secure buffer (part_opstr)
1402 and build the sublist with the wrapping data for the current operation, which will be used by umlclass_draw(), too.
1404 pos_next_comma = pos_brace = wrap_pos = offset
1405 = maxlinewidth = umlclass->max_wrapped_line_width = 0;
1406 while( wrap_pos + offset < length)
1410 pos_next_comma = strcspn( (const gchar*)opstr + wrap_pos + offset, ",");
1411 wrap_pos += pos_next_comma + 1;
1412 } while( wrap_pos < umlclass->wrap_after_char - pos_brace
1413 && wrap_pos + offset < length);
1415 if( offset == 0){
1416 pos_brace = strcspn( opstr, "(");
1417 op->wrap_indent = pos_brace + 1;
1419 op->wrappos = g_list_append(op->wrappos,
1420 GINT_TO_POINTER(wrap_pos + offset));
1422 maxlinewidth = MAX(maxlinewidth, wrap_pos);
1424 offset += wrap_pos;
1425 wrap_pos = 0;
1427 umlclass->max_wrapped_line_width = MAX( umlclass->max_wrapped_line_width, maxlinewidth+1);
1429 indent = op->wrap_indent;
1430 part_opstr = g_alloca(umlclass->max_wrapped_line_width+indent+1);
1432 wrapsublist = op->wrappos;
1433 wrap_pos = last_wrap_pos = 0;
1435 while( wrapsublist != NULL){
1436 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
1437 if( last_wrap_pos == 0){
1438 strncpy( part_opstr, opstr, wrap_pos);
1439 memset( part_opstr+wrap_pos, '\0', 1);
1441 else
1443 memset( part_opstr, ' ', indent);
1444 memset( part_opstr+indent, '\0', 1);
1445 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
1448 width = dia_font_string_width(part_opstr,Font,FontHeight);
1449 umlclass->operationsbox_height += op->ascent;
1450 if (last_wrap_pos == 0) {
1451 umlclass->operationsbox_height += (FontHeight - op->ascent);
1454 maxwidth = MAX(width, maxwidth);
1455 last_wrap_pos = wrap_pos;
1456 wrapsublist = g_list_next( wrapsublist);
1459 else
1461 op->needs_wrapping = FALSE;
1465 if (!(umlclass->wrap_operations && length > umlclass->wrap_after_char)) {
1466 switch(op->inheritance_type)
1468 case UML_ABSTRACT:
1469 Font = umlclass->abstract_font;
1470 FontHeight = umlclass->abstract_font_height;
1471 break;
1472 case UML_POLYMORPHIC:
1473 Font = umlclass->polymorphic_font;
1474 FontHeight = umlclass->polymorphic_font_height;
1475 break;
1476 case UML_LEAF:
1477 default:
1478 Font = umlclass->normal_font;
1479 FontHeight = umlclass->font_height;
1481 width = dia_font_string_width(opstr,Font,FontHeight);
1482 umlclass->operationsbox_height += FontHeight;
1484 maxwidth = MAX(width, maxwidth);
1487 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1488 int NumberOfLines = 0;
1489 gchar *Wrapped = uml_create_documentation_tag(op->comment,
1490 umlclass->comment_tagging,
1491 umlclass->comment_line_length,
1492 &NumberOfLines);
1494 width = dia_font_string_width(Wrapped,
1495 umlclass->comment_font,
1496 umlclass->comment_font_height);
1498 g_free(Wrapped);
1499 umlclass->operationsbox_height +=
1500 (umlclass->comment_font_height * (NumberOfLines+1));
1502 maxwidth = MAX(width, maxwidth);
1505 i++;
1506 list = g_list_next(list);
1507 g_free (opstr);
1511 umlclass->element.width = maxwidth + 2*0.3;
1513 if ((umlclass->operationsbox_height<0.4) || umlclass->suppress_operations ) {
1514 umlclass->operationsbox_height = 0.4;
1517 return maxwidth;
1521 * calculate the size of the class icon for an object of UMLClass.
1522 * This is done by calculating the size of the text to be displayed within
1523 * each of the contained bounding boxes, name, attributes and operations.
1524 * Because the comments may require wrapping, each comment is wrapped and
1525 * the resulting number of lines is used to calculate the size of the
1526 * comment within the box. The various font settings with in the class
1527 * properties contribute to the overall size of the resulting bounding box.
1529 * * @param umlclass a pointer to an object of UMLClass
1532 void
1533 umlclass_calculate_data(UMLClass *umlclass)
1535 int i;
1536 int num_templates;
1537 real maxwidth = 0.0;
1538 real width;
1539 GList *list;
1541 if (!umlclass->destroyed)
1543 maxwidth = MAX(umlclass_calculate_name_data(umlclass), maxwidth);
1545 umlclass->element.height = umlclass->namebox_height;
1547 if (umlclass->visible_attributes){
1548 maxwidth = MAX(umlclass_calculate_attribute_data(umlclass), maxwidth);
1549 umlclass->element.height += umlclass->attributesbox_height;
1551 if (umlclass->visible_operations){
1552 maxwidth = MAX(umlclass_calculate_operation_data(umlclass), maxwidth);
1553 umlclass->element.height += umlclass->operationsbox_height;
1555 umlclass->element.width = maxwidth+0.5;
1556 /* templates box: */
1557 num_templates = g_list_length(umlclass->formal_params);
1559 umlclass->templates_height =
1560 umlclass->font_height * num_templates + 2*0.1;
1561 umlclass->templates_height = MAX(umlclass->templates_height, 0.4);
1564 maxwidth = 2.3;
1565 if (num_templates != 0)
1567 i = 0;
1568 list = umlclass->formal_params;
1569 while (list != NULL)
1571 UMLFormalParameter *param = (UMLFormalParameter *) list->data;
1572 gchar *paramstr = uml_get_formalparameter_string(param);
1574 width = dia_font_string_width(paramstr,
1575 umlclass->normal_font,
1576 umlclass->font_height);
1577 maxwidth = MAX(width, maxwidth);
1579 i++;
1580 list = g_list_next(list);
1581 g_free (paramstr);
1584 umlclass->templates_width = maxwidth + 2*0.2;
1588 static void
1589 fill_in_fontdata(UMLClass *umlclass)
1591 if (umlclass->normal_font == NULL) {
1592 umlclass->font_height = 0.8;
1593 umlclass->normal_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, 0.8);
1595 if (umlclass->abstract_font == NULL) {
1596 umlclass->abstract_font_height = 0.8;
1597 umlclass->abstract_font =
1598 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD, 0.8);
1600 if (umlclass->polymorphic_font == NULL) {
1601 umlclass->polymorphic_font_height = 0.8;
1602 umlclass->polymorphic_font =
1603 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC, 0.8);
1605 if (umlclass->classname_font == NULL) {
1606 umlclass->classname_font_height = 1.0;
1607 umlclass->classname_font =
1608 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD, 1.0);
1610 if (umlclass->abstract_classname_font == NULL) {
1611 umlclass->abstract_classname_font_height = 1.0;
1612 umlclass->abstract_classname_font =
1613 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC, 1.0);
1615 if (umlclass->comment_font == NULL) {
1616 umlclass->comment_font_height = 0.7;
1617 umlclass->comment_font = dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1621 * Create an object of type class
1622 * By default this will create a object of class UMLClass. Howerver there
1623 * are at least two types of UMLClass objects, so the user_data is selects
1624 * the correct UMLClass object. Other than that this is quite straight
1625 * forward. The key to the polymorphic nature of this object is the use of
1626 * the DiaObjectType record which in conjunction with the user_data
1627 * controls the specific derived object type.
1629 * @param startpoint the origin of the object being created
1630 * @param user_data Information used by this routine to create the appropriate object
1631 * @param handle1 ignored when creating a class object
1632 * @param handle2 ignored when creating a class object
1633 * @return a pointer to the object created
1635 * NOTE:
1636 * This function should most likely be move to a source file for
1637 * handling global UML functionallity at some point.
1640 static DiaObject *
1641 umlclass_create(Point *startpoint,
1642 void *user_data,
1643 Handle **handle1,
1644 Handle **handle2)
1646 UMLClass *umlclass;
1647 Element *elem;
1648 DiaObject *obj;
1649 int i;
1651 umlclass = g_malloc0(sizeof(UMLClass));
1652 elem = &umlclass->element;
1653 obj = &elem->object;
1656 elem->corner = *startpoint;
1658 #ifdef UML_MAINPOINT
1659 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1); /* No attribs or ops => 0 extra connectionpoints. */
1660 #else
1661 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS); /* No attribs or ops => 0 extra connectionpoints. */
1662 #endif
1664 umlclass->properties_dialog = NULL;
1665 fill_in_fontdata(umlclass);
1669 * The following block of code may need to be converted to a switch statement if more than
1670 * two types of objects can be made - Dave Klotzbach
1672 umlclass->template = (GPOINTER_TO_INT(user_data)==1);
1674 if (umlclass->template){
1675 umlclass->name = g_strdup (_("Template"));
1677 else {
1678 umlclass->name = g_strdup (_("Class"));
1680 obj->type = &umlclass_type;
1681 obj->ops = &umlclass_ops;
1683 umlclass->stereotype = NULL;
1684 umlclass->comment = NULL;
1686 umlclass->abstract = FALSE;
1688 umlclass->suppress_attributes = FALSE;
1689 umlclass->suppress_operations = FALSE;
1691 umlclass->visible_attributes = TRUE;
1692 umlclass->visible_operations = TRUE;
1693 umlclass->visible_comments = FALSE;
1695 umlclass->wrap_operations = TRUE;
1696 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
1698 umlclass->attributes = NULL;
1700 umlclass->operations = NULL;
1702 umlclass->formal_params = NULL;
1704 umlclass->stereotype_string = NULL;
1706 umlclass->text_color = color_black;
1707 umlclass->line_color = attributes_get_foreground();
1708 umlclass->fill_color = attributes_get_background();
1710 umlclass_calculate_data(umlclass);
1712 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1713 obj->connections[i] = &umlclass->connections[i];
1714 umlclass->connections[i].object = obj;
1715 umlclass->connections[i].connected = NULL;
1717 #ifdef UML_MAINPOINT
1718 /* Put mainpoint at the end, after conditional attr/oprn points,
1719 * but store it in the local connectionpoint array. */
1720 i += umlclass_num_dynamic_connectionpoints(umlclass);
1721 obj->connections[i] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
1722 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = obj;
1723 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1724 #endif
1726 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
1727 umlclass_update_data(umlclass);
1729 for (i=0;i<8;i++) {
1730 obj->handles[i]->type = HANDLE_NON_MOVABLE;
1733 *handle1 = NULL;
1734 *handle2 = NULL;
1735 return &umlclass->element.object;
1738 static void
1739 umlclass_destroy(UMLClass *umlclass)
1741 GList *list;
1742 UMLAttribute *attr;
1743 UMLOperation *op;
1744 UMLFormalParameter *param;
1746 umlclass_sanity_check(umlclass, "Destroying");
1748 umlclass->destroyed = TRUE;
1750 dia_font_unref(umlclass->normal_font);
1751 dia_font_unref(umlclass->abstract_font);
1752 dia_font_unref(umlclass->polymorphic_font);
1753 dia_font_unref(umlclass->classname_font);
1754 dia_font_unref(umlclass->abstract_classname_font);
1755 dia_font_unref(umlclass->comment_font);
1757 element_destroy(&umlclass->element);
1759 g_free(umlclass->name);
1760 g_free(umlclass->stereotype);
1761 g_free(umlclass->comment);
1763 list = umlclass->attributes;
1764 while (list != NULL) {
1765 attr = (UMLAttribute *)list->data;
1766 g_free(attr->left_connection);
1767 g_free(attr->right_connection);
1768 uml_attribute_destroy(attr);
1769 list = g_list_next(list);
1771 g_list_free(umlclass->attributes);
1773 list = umlclass->operations;
1774 while (list != NULL) {
1775 op = (UMLOperation *)list->data;
1776 g_free(op->left_connection);
1777 g_free(op->right_connection);
1778 uml_operation_destroy(op);
1779 list = g_list_next(list);
1781 g_list_free(umlclass->operations);
1783 list = umlclass->formal_params;
1784 while (list != NULL) {
1785 param = (UMLFormalParameter *)list->data;
1786 uml_formalparameter_destroy(param);
1787 list = g_list_next(list);
1789 g_list_free(umlclass->formal_params);
1791 if (umlclass->stereotype_string != NULL) {
1792 g_free(umlclass->stereotype_string);
1795 if (umlclass->properties_dialog != NULL) {
1796 g_list_free(umlclass->properties_dialog->deleted_connections);
1797 gtk_widget_destroy(umlclass->properties_dialog->dialog);
1798 g_free(umlclass->properties_dialog);
1802 static DiaObject *
1803 umlclass_copy(UMLClass *umlclass)
1805 int i;
1806 UMLClass *newumlclass;
1807 Element *elem, *newelem;
1808 DiaObject *newobj;
1809 GList *list;
1810 UMLFormalParameter *param;
1812 elem = &umlclass->element;
1814 newumlclass = g_malloc0(sizeof(UMLClass));
1815 newelem = &newumlclass->element;
1816 newobj = &newelem->object;
1818 element_copy(elem, newelem);
1820 newumlclass->font_height = umlclass->font_height;
1821 newumlclass->abstract_font_height = umlclass->abstract_font_height;
1822 newumlclass->polymorphic_font_height = umlclass->polymorphic_font_height;
1823 newumlclass->classname_font_height = umlclass->classname_font_height;
1824 newumlclass->abstract_classname_font_height =
1825 umlclass->abstract_classname_font_height;
1826 newumlclass->comment_font_height =
1827 umlclass->comment_font_height;
1829 newumlclass->normal_font =
1830 dia_font_ref(umlclass->normal_font);
1831 newumlclass->abstract_font =
1832 dia_font_ref(umlclass->abstract_font);
1833 newumlclass->polymorphic_font =
1834 dia_font_ref(umlclass->polymorphic_font);
1835 newumlclass->classname_font =
1836 dia_font_ref(umlclass->classname_font);
1837 newumlclass->abstract_classname_font =
1838 dia_font_ref(umlclass->abstract_classname_font);
1839 newumlclass->comment_font =
1840 dia_font_ref(umlclass->comment_font);
1842 newumlclass->name = g_strdup(umlclass->name);
1843 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0')
1844 newumlclass->stereotype = g_strdup(umlclass->stereotype);
1845 else
1846 newumlclass->stereotype = NULL;
1848 if (umlclass->comment != NULL)
1849 newumlclass->comment = g_strdup(umlclass->comment);
1850 else
1851 newumlclass->comment = NULL;
1853 newumlclass->abstract = umlclass->abstract;
1854 newumlclass->suppress_attributes = umlclass->suppress_attributes;
1855 newumlclass->suppress_operations = umlclass->suppress_operations;
1856 newumlclass->visible_attributes = umlclass->visible_attributes;
1857 newumlclass->visible_operations = umlclass->visible_operations;
1858 newumlclass->visible_comments = umlclass->visible_comments;
1859 newumlclass->wrap_operations = umlclass->wrap_operations;
1860 newumlclass->wrap_after_char = umlclass->wrap_after_char;
1861 newumlclass->comment_line_length = umlclass->comment_line_length;
1862 newumlclass->comment_tagging = umlclass->comment_tagging;
1863 newumlclass->text_color = umlclass->text_color;
1864 newumlclass->line_color = umlclass->line_color;
1865 newumlclass->fill_color = umlclass->fill_color;
1867 newumlclass->attributes = NULL;
1868 list = umlclass->attributes;
1869 while (list != NULL) {
1870 UMLAttribute *attr = (UMLAttribute *)list->data;
1871 /* not copying the connection, if there was one */
1872 UMLAttribute *newattr = uml_attribute_copy(attr);
1873 uml_attribute_ensure_connection_points (newattr, newobj);
1875 newumlclass->attributes = g_list_append(newumlclass->attributes,
1876 newattr);
1877 list = g_list_next(list);
1880 newumlclass->operations = NULL;
1881 list = umlclass->operations;
1882 while (list != NULL) {
1883 UMLOperation *op = (UMLOperation *)list->data;
1884 UMLOperation *newop = uml_operation_copy(op);
1885 uml_operation_ensure_connection_points (newop, newobj);
1887 newumlclass->operations = g_list_append(newumlclass->operations,
1888 newop);
1889 list = g_list_next(list);
1892 newumlclass->template = umlclass->template;
1894 newumlclass->formal_params = NULL;
1895 list = umlclass->formal_params;
1896 while (list != NULL) {
1897 param = (UMLFormalParameter *)list->data;
1898 newumlclass->formal_params =
1899 g_list_append(newumlclass->formal_params,
1900 uml_formalparameter_copy(param));
1901 list = g_list_next(list);
1904 newumlclass->properties_dialog = NULL;
1906 newumlclass->stereotype_string = NULL;
1908 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1909 newobj->connections[i] = &newumlclass->connections[i];
1910 newumlclass->connections[i].object = newobj;
1911 newumlclass->connections[i].connected = NULL;
1912 newumlclass->connections[i].pos = umlclass->connections[i].pos;
1913 newumlclass->connections[i].last_pos = umlclass->connections[i].last_pos;
1916 umlclass_calculate_data(newumlclass);
1918 i = UMLCLASS_CONNECTIONPOINTS;
1919 if ( (newumlclass->visible_attributes) &&
1920 (!newumlclass->suppress_attributes)) {
1921 list = newumlclass->attributes;
1922 while (list != NULL) {
1923 UMLAttribute *attr = (UMLAttribute *)list->data;
1924 newobj->connections[i++] = attr->left_connection;
1925 newobj->connections[i++] = attr->right_connection;
1927 list = g_list_next(list);
1931 if ( (newumlclass->visible_operations) &&
1932 (!newumlclass->suppress_operations)) {
1933 list = newumlclass->operations;
1934 while (list != NULL) {
1935 UMLOperation *op = (UMLOperation *)list->data;
1936 newobj->connections[i++] = op->left_connection;
1937 newobj->connections[i++] = op->right_connection;
1939 list = g_list_next(list);
1943 #ifdef UML_MAINPOINT
1944 newobj->connections[i] = &newumlclass->connections[UMLCLASS_CONNECTIONPOINTS];
1945 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = newobj;
1946 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1947 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos =
1948 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos;
1949 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos =
1950 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos;
1951 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags =
1952 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags;
1953 i++;
1954 #endif
1956 umlclass_update_data(newumlclass);
1958 umlclass_sanity_check(newumlclass, "Copied");
1960 return &newumlclass->element.object;
1964 static void
1965 umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
1966 const char *filename)
1968 UMLAttribute *attr;
1969 UMLOperation *op;
1970 UMLFormalParameter *formal_param;
1971 GList *list;
1972 AttributeNode attr_node;
1974 umlclass_sanity_check(umlclass, "Saving");
1976 element_save(&umlclass->element, obj_node);
1978 /* Class info: */
1979 data_add_string(new_attribute(obj_node, "name"),
1980 umlclass->name);
1981 data_add_string(new_attribute(obj_node, "stereotype"),
1982 umlclass->stereotype);
1983 data_add_string(new_attribute(obj_node, "comment"),
1984 umlclass->comment);
1985 data_add_boolean(new_attribute(obj_node, "abstract"),
1986 umlclass->abstract);
1987 data_add_boolean(new_attribute(obj_node, "suppress_attributes"),
1988 umlclass->suppress_attributes);
1989 data_add_boolean(new_attribute(obj_node, "suppress_operations"),
1990 umlclass->suppress_operations);
1991 data_add_boolean(new_attribute(obj_node, "visible_attributes"),
1992 umlclass->visible_attributes);
1993 data_add_boolean(new_attribute(obj_node, "visible_operations"),
1994 umlclass->visible_operations);
1995 data_add_boolean(new_attribute(obj_node, "visible_comments"),
1996 umlclass->visible_comments);
1997 data_add_boolean(new_attribute(obj_node, "wrap_operations"),
1998 umlclass->wrap_operations);
1999 data_add_int(new_attribute(obj_node, "wrap_after_char"),
2000 umlclass->wrap_after_char);
2001 data_add_int(new_attribute(obj_node, "comment_line_length"),
2002 umlclass->comment_line_length);
2003 data_add_boolean(new_attribute(obj_node, "comment_tagging"),
2004 umlclass->comment_tagging);
2005 data_add_color(new_attribute(obj_node, "line_color"),
2006 &umlclass->line_color);
2007 data_add_color(new_attribute(obj_node, "fill_color"),
2008 &umlclass->fill_color);
2009 data_add_color(new_attribute(obj_node, "text_color"),
2010 &umlclass->text_color);
2011 data_add_font (new_attribute (obj_node, "normal_font"),
2012 umlclass->normal_font);
2013 data_add_font (new_attribute (obj_node, "abstract_font"),
2014 umlclass->abstract_font);
2015 data_add_font (new_attribute (obj_node, "polymorphic_font"),
2016 umlclass->polymorphic_font);
2017 data_add_font (new_attribute (obj_node, "classname_font"),
2018 umlclass->classname_font);
2019 data_add_font (new_attribute (obj_node, "abstract_classname_font"),
2020 umlclass->abstract_classname_font);
2021 data_add_font (new_attribute (obj_node, "comment_font"),
2022 umlclass->comment_font);
2023 data_add_real (new_attribute (obj_node, "normal_font_height"),
2024 umlclass->font_height);
2025 data_add_real (new_attribute (obj_node, "polymorphic_font_height"),
2026 umlclass->polymorphic_font_height);
2027 data_add_real (new_attribute (obj_node, "abstract_font_height"),
2028 umlclass->abstract_font_height);
2029 data_add_real (new_attribute (obj_node, "classname_font_height"),
2030 umlclass->classname_font_height);
2031 data_add_real (new_attribute (obj_node, "abstract_classname_font_height"),
2032 umlclass->abstract_classname_font_height);
2033 data_add_real (new_attribute (obj_node, "comment_font_height"),
2034 umlclass->comment_font_height);
2036 /* Attribute info: */
2037 attr_node = new_attribute(obj_node, "attributes");
2038 list = umlclass->attributes;
2039 while (list != NULL) {
2040 attr = (UMLAttribute *) list->data;
2041 uml_attribute_write(attr_node, attr);
2042 list = g_list_next(list);
2045 /* Operations info: */
2046 attr_node = new_attribute(obj_node, "operations");
2047 list = umlclass->operations;
2048 while (list != NULL) {
2049 op = (UMLOperation *) list->data;
2050 uml_operation_write(attr_node, op);
2051 list = g_list_next(list);
2054 /* Template info: */
2055 data_add_boolean(new_attribute(obj_node, "template"),
2056 umlclass->template);
2058 attr_node = new_attribute(obj_node, "templates");
2059 list = umlclass->formal_params;
2060 while (list != NULL) {
2061 formal_param = (UMLFormalParameter *) list->data;
2062 uml_formalparameter_write(attr_node, formal_param);
2063 list = g_list_next(list);
2067 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
2068 const char *filename)
2070 UMLClass *umlclass;
2071 Element *elem;
2072 DiaObject *obj;
2073 AttributeNode attr_node;
2074 int i;
2075 GList *list;
2078 umlclass = g_malloc0(sizeof(UMLClass));
2079 elem = &umlclass->element;
2080 obj = &elem->object;
2082 obj->type = &umlclass_type;
2083 obj->ops = &umlclass_ops;
2085 element_load(elem, obj_node);
2087 #ifdef UML_MAINPOINT
2088 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1);
2089 #else
2090 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS);
2091 #endif
2093 umlclass->properties_dialog = NULL;
2095 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2096 obj->connections[i] = &umlclass->connections[i];
2097 umlclass->connections[i].object = obj;
2098 umlclass->connections[i].connected = NULL;
2101 fill_in_fontdata(umlclass);
2103 /* kind of dirty, object_load_props() may leave us in an inconsistent state --hb */
2104 object_load_props(obj,obj_node);
2106 /* parameters loaded via StdProp dont belong here anymore. In case of strings they
2107 * will produce leaks. Otherwise the are just wasteing time (at runtime and while
2108 * reading the code). Except maybe for some compatibility stuff.
2109 * Although that *could* probably done via StdProp too. --hb
2112 /* new since 0.94, don't wrap by default to keep old diagrams intact */
2113 umlclass->wrap_operations = FALSE;
2114 attr_node = object_find_attribute(obj_node, "wrap_operations");
2115 if (attr_node != NULL)
2116 umlclass->wrap_operations = data_boolean(attribute_first_data(attr_node));
2118 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
2119 attr_node = object_find_attribute(obj_node, "wrap_after_char");
2120 if (attr_node != NULL)
2121 umlclass->wrap_after_char = data_int(attribute_first_data(attr_node));
2123 /* if it uses the new name the value is already set by object_load_props() above */
2124 umlclass->comment_line_length = UMLCLASS_COMMENT_LINE_LENGTH;
2125 attr_node = object_find_attribute(obj_node,"comment_line_length");
2126 /* support the unusal cased name, although it only existed in cvs version */
2127 if (attr_node == NULL)
2128 attr_node = object_find_attribute(obj_node,"Comment_line_length");
2129 if (attr_node != NULL)
2130 umlclass->comment_line_length = data_int(attribute_first_data(attr_node));
2132 /* compatibility with 0.94 and before as well as the temporary state with only 'comment_line_length' */
2133 umlclass->comment_tagging = (attr_node != NULL);
2134 attr_node = object_find_attribute(obj_node, "comment_tagging");
2135 if (attr_node != NULL)
2136 umlclass->comment_tagging = data_boolean(attribute_first_data(attr_node));
2138 umlclass->line_color = color_black;
2139 /* support the old name ... */
2140 attr_node = object_find_attribute(obj_node, "foreground_color");
2141 if(attr_node != NULL)
2142 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2143 umlclass->text_color = umlclass->line_color;
2144 /* ... but prefer the new one */
2145 attr_node = object_find_attribute(obj_node, "line_color");
2146 if(attr_node != NULL)
2147 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2148 attr_node = object_find_attribute(obj_node, "text_color");
2149 if(attr_node != NULL)
2150 data_color(attribute_first_data(attr_node), &umlclass->text_color);
2152 umlclass->fill_color = color_white;
2153 /* support the old name ... */
2154 attr_node = object_find_attribute(obj_node, "background_color");
2155 if(attr_node != NULL)
2156 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2157 /* ... but prefer the new one */
2158 attr_node = object_find_attribute(obj_node, "fill_color");
2159 if(attr_node != NULL)
2160 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2162 /* Attribute info: */
2163 list = umlclass->attributes;
2164 while (list) {
2165 UMLAttribute *attr = list->data;
2166 g_assert(attr);
2168 uml_attribute_ensure_connection_points (attr, obj);
2169 list = g_list_next(list);
2172 /* Operations info: */
2173 list = umlclass->operations;
2174 while (list) {
2175 UMLOperation *op = (UMLOperation *)list->data;
2176 g_assert(op);
2178 uml_operation_ensure_connection_points (op, obj);
2179 list = g_list_next(list);
2182 /* Template info: */
2183 umlclass->template = FALSE;
2184 attr_node = object_find_attribute(obj_node, "template");
2185 if (attr_node != NULL)
2186 umlclass->template = data_boolean(attribute_first_data(attr_node));
2188 fill_in_fontdata(umlclass);
2190 umlclass->stereotype_string = NULL;
2192 umlclass_calculate_data(umlclass);
2194 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
2195 umlclass_update_data(umlclass);
2197 for (i=0;i<8;i++) {
2198 obj->handles[i]->type = HANDLE_NON_MOVABLE;
2201 umlclass_sanity_check(umlclass, "Loaded class");
2203 return &umlclass->element.object;
2206 /** Returns the number of connection points used by the attributes and
2207 * connections in the current state of the object.
2209 static int
2210 umlclass_num_dynamic_connectionpoints(UMLClass *umlclass) {
2211 int num = 0;
2212 if ( (umlclass->visible_attributes) &&
2213 (!umlclass->suppress_attributes)) {
2214 GList *list = umlclass->attributes;
2215 num += 2 * g_list_length(list);
2218 if ( (umlclass->visible_operations) &&
2219 (!umlclass->suppress_operations)) {
2220 GList *list = umlclass->operations;
2221 num += 2 * g_list_length(list);
2223 return num;
2226 void
2227 umlclass_sanity_check(UMLClass *c, gchar *msg)
2229 #ifdef UML_MAINPOINT
2230 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS + 1;
2231 #else
2232 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS;
2233 #endif
2234 DiaObject *obj = (DiaObject*)c;
2235 GList *attrs, *ops;
2236 int i;
2238 dia_object_sanity_check((DiaObject *)c, msg);
2240 /* Check that num_connections is correct */
2241 dia_assert_true(num_fixed_connections + umlclass_num_dynamic_connectionpoints(c)
2242 == obj->num_connections,
2243 "%s: Class %p has %d connections, but %d fixed and %d dynamic\n",
2244 msg, c, obj->num_connections, num_fixed_connections,
2245 umlclass_num_dynamic_connectionpoints(c));
2247 for (i = 0; i < UMLCLASS_CONNECTIONPOINTS; i++) {
2248 dia_assert_true(&c->connections[i] == obj->connections[i],
2249 "%s: Class %p connection mismatch at %d: %p != %p\n",
2250 msg, c, i, &c->connections[i], obj->connections[i]);
2253 #ifdef UML_MAINPOINT
2254 dia_assert_true(&c->connections[i] ==
2255 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2256 "%s: Class %p mainpoint mismatch: %p != %p (at %d)\n",
2257 msg, c, i, &c->connections[i],
2258 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2259 i + umlclass_num_dynamic_connectionpoints(c));
2260 #endif
2262 /* Check that attributes are set up right. */
2263 i = 0;
2264 for (attrs = c->attributes; attrs != NULL; attrs = g_list_next(attrs)) {
2265 UMLAttribute *attr = (UMLAttribute *)attrs->data;
2267 dia_assert_true(attr->name != NULL,
2268 "%s: %p attr %d has null name\n",
2269 msg, c, i);
2270 dia_assert_true(attr->type != NULL,
2271 "%s: %p attr %d has null type\n",
2272 msg, c, i);
2273 #if 0 /* attr->comment == NULL is fine everywhere else */
2274 dia_assert_true(attr->comment != NULL,
2275 "%s: %p attr %d has null comment\n",
2276 msg, c, i);
2277 #endif
2279 /* the following checks are only right with visible attributes */
2280 if (c->visible_attributes && !c->suppress_attributes) {
2281 int conn_offset = UMLCLASS_CONNECTIONPOINTS + 2 * i;
2283 dia_assert_true(attr->left_connection != NULL,
2284 "%s: %p attr %d has null left connection\n",
2285 msg, c, i);
2286 dia_assert_true(attr->right_connection != NULL,
2287 "%s: %p attr %d has null right connection\n",
2288 msg, c, i);
2290 dia_assert_true(attr->left_connection == obj->connections[conn_offset],
2291 "%s: %p attr %d left conn %p doesn't match obj conn %d: %p\n",
2292 msg, c, i, attr->left_connection,
2293 conn_offset, obj->connections[conn_offset]);
2294 dia_assert_true(attr->right_connection == obj->connections[conn_offset + 1],
2295 "%s: %p attr %d right conn %p doesn't match obj conn %d: %p\n",
2296 msg, c, i, attr->right_connection,
2297 conn_offset + 1, obj->connections[conn_offset + 1]);
2298 i++;
2301 /* Check that operations are set up right. */