Template rendering improvements.
[dia.git] / objects / UML / class.c
blobf7a2d1207858a96b3aa3c566da442ab70e60fc39
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 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <assert.h>
28 #include <gtk/gtk.h>
29 #include <math.h>
30 #include <string.h>
31 #include <ctype.h>
33 #include "intl.h"
34 #include "diarenderer.h"
35 #include "attributes.h"
36 #include "properties.h"
37 #include "diamenu.h"
39 #include "class.h"
41 #include "pixmaps/umlclass.xpm"
43 #include "debug.h"
45 #define UMLCLASS_BORDER 0.1
46 #define UMLCLASS_UNDERLINEWIDTH 0.05
47 #define UMLCLASS_TEMPLATE_OVERLAY_X 2.3
48 #define UMLCLASS_TEMPLATE_OVERLAY_Y 0.3
50 static real umlclass_distance_from(UMLClass *umlclass, Point *point);
51 static void umlclass_select(UMLClass *umlclass, Point *clicked_point,
52 DiaRenderer *interactive_renderer);
53 static ObjectChange* umlclass_move_handle(UMLClass *umlclass, Handle *handle,
54 Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers);
55 static ObjectChange* umlclass_move(UMLClass *umlclass, Point *to);
56 static void umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer);
57 static DiaObject *umlclass_create(Point *startpoint,
58 void *user_data,
59 Handle **handle1,
60 Handle **handle2);
61 static void umlclass_destroy(UMLClass *umlclass);
62 static DiaObject *umlclass_copy(UMLClass *umlclass);
64 static void umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
65 const char *filename);
66 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
67 const char *filename);
69 static DiaMenu * umlclass_object_menu(DiaObject *obj, Point *p);
70 static ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data);
72 static PropDescription *umlclass_describe_props(UMLClass *umlclass);
73 static void umlclass_get_props(UMLClass *umlclass, GPtrArray *props);
74 static void umlclass_set_props(UMLClass *umlclass, GPtrArray *props);
76 static void fill_in_fontdata(UMLClass *umlclass);
77 static int umlclass_num_dynamic_connectionpoints(UMLClass *class);
79 static ObjectTypeOps umlclass_type_ops =
81 (CreateFunc) umlclass_create,
82 (LoadFunc) umlclass_load,
83 (SaveFunc) umlclass_save
87 * This is the type descriptor for a UML - Class. It contains the
88 * information used by Dia to create an object of this type. The structure
89 * of this data type is defined in the header file object.h. When a
90 * derivation of class is required, then this type can be copied and then
91 * change the name and any other fields that are variances from the base
92 * 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 static ObjectOps umlclass_ops = {
107 (DestroyFunc) umlclass_destroy,
108 (DrawFunc) umlclass_draw,
109 (DistanceFunc) umlclass_distance_from,
110 (SelectFunc) umlclass_select,
111 (CopyFunc) umlclass_copy,
112 (MoveFunc) umlclass_move,
113 (MoveHandleFunc) umlclass_move_handle,
114 (GetPropertiesFunc) umlclass_get_properties,
115 (ApplyPropertiesFunc) umlclass_apply_props_from_dialog,
116 (ObjectMenuFunc) umlclass_object_menu,
117 (DescribePropsFunc) umlclass_describe_props,
118 (GetPropsFunc) umlclass_get_props,
119 (SetPropsFunc) umlclass_set_props
122 extern PropDescDArrayExtra umlattribute_extra;
123 extern PropDescDArrayExtra umloperation_extra;
124 extern PropDescDArrayExtra umlparameter_extra;
125 extern PropDescDArrayExtra umlformalparameter_extra;
127 static PropDescription umlclass_props[] = {
128 ELEMENT_COMMON_PROPERTIES,
129 PROP_STD_TEXT_COLOUR_OPTIONAL,
130 PROP_STD_LINE_COLOUR_OPTIONAL,
131 PROP_STD_FILL_COLOUR_OPTIONAL,
133 PROP_STD_NOTEBOOK_BEGIN,
134 PROP_NOTEBOOK_PAGE("class", PROP_FLAG_DONT_MERGE, N_("Class")),
135 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
136 N_("Name"), NULL, NULL },
137 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
138 N_("Stereotype"), NULL, NULL },
139 { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
140 N_("Comment"), NULL, NULL },
141 { "abstract", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
142 N_("Abstract"), NULL, NULL },
143 { "template", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL| PROP_FLAG_NO_DEFAULTS,
144 N_("Template"), NULL, NULL },
146 { "suppress_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
147 N_("Suppress Attributes"), NULL, NULL },
148 { "suppress_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
149 N_("Suppress Operations"), NULL, NULL },
150 { "visible_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
151 N_("Visible Attributes"), NULL, NULL },
152 { "visible_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
153 N_("Visible Operations"), NULL, NULL },
154 { "visible_comments", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
155 N_("Visible Comments"), NULL, NULL },
156 { "wrap_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
157 N_("Wrap Operations"), NULL, NULL },
158 { "wrap_after_char", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
159 N_("Wrap after char"), NULL, NULL },
160 { "Comment_line_length", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
161 N_("Comment line length"), NULL, NULL},
163 /* all this just to make the defaults selectable ... */
164 PROP_NOTEBOOK_PAGE("font", PROP_FLAG_DONT_MERGE, N_("Font")),
165 PROP_STD_MULTICOL_BEGIN,
166 PROP_MULTICOL_COLUMN("font"),
167 /* FIXME: apparently multicol does not work correctly, this should be FIRST column */
168 { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
169 N_("Normal"), NULL, NULL },
170 { "polymorphic_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
171 N_("Polymorphic"), NULL, NULL },
172 { "abstract_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
173 N_("Abstract"), NULL, NULL },
174 { "classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
175 N_("Classname"), NULL, NULL },
176 { "abstract_classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
177 N_("Abstract Classname"), NULL, NULL },
178 { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
179 N_("Comment"), NULL, NULL },
181 PROP_MULTICOL_COLUMN("height"),
182 { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
183 N_(" "), NULL, NULL },
184 { "polymorphic_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
185 N_(" "), NULL, NULL },
186 { "abstract_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
187 N_(" "), NULL, NULL },
188 { "classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
189 N_(" "), NULL, NULL },
190 { "abstract_classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
191 N_(" "), NULL, NULL },
192 { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
193 N_(" "), NULL, NULL },
194 PROP_STD_MULTICOL_END,
195 PROP_STD_NOTEBOOK_END,
197 /* these are used during load, but currently not during save */
198 { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
199 N_("Attributes"), NULL, NULL /* umlattribute_extra */ },
200 { "operations", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
201 N_("Operations"), NULL, NULL /* umloperations_extra */ },
202 /* the naming is questionable, but kept for compatibility */
203 { "templates", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
204 N_("Template Parameters"), NULL, NULL /* umlformalparameters_extra */ },
206 PROP_DESC_END
209 static PropDescription *
210 umlclass_describe_props(UMLClass *umlclass)
212 if (umlclass_props[0].quark == 0) {
213 int i = 0;
215 prop_desc_list_calculate_quarks(umlclass_props);
216 while (umlclass_props[i].name != NULL) {
217 /* can't do this static, at least not on win32
218 * due to relocation (initializer not a constant)
220 if (0 == strcmp(umlclass_props[i].name, "attributes"))
221 umlclass_props[i].extra_data = &umlattribute_extra;
222 else if (0 == strcmp(umlclass_props[i].name, "operations")) {
223 PropDescription *records = umloperation_extra.common.record;
224 int j = 0;
226 umlclass_props[i].extra_data = &umloperation_extra;
227 while (records[j].name != NULL) {
228 if (0 == strcmp(records[j].name, "parameters"))
229 records[j].extra_data = &umlparameter_extra;
230 j++;
233 else if (0 == strcmp(umlclass_props[i].name, "templates"))
234 umlclass_props[i].extra_data = &umlformalparameter_extra;
236 i++;
239 return umlclass_props;
242 static PropOffset umlclass_offsets[] = {
243 ELEMENT_COMMON_PROPERTIES_OFFSETS,
245 { "text_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, text_color) },
246 { "line_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, line_color) },
247 { "fill_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, fill_color) },
248 { "name", PROP_TYPE_STRING, offsetof(UMLClass, name) },
249 { "stereotype", PROP_TYPE_STRING, offsetof(UMLClass, stereotype) },
250 { "comment", PROP_TYPE_STRING, offsetof(UMLClass, comment) },
251 { "abstract", PROP_TYPE_BOOL, offsetof(UMLClass, abstract) },
252 { "template", PROP_TYPE_BOOL, offsetof(UMLClass, template) },
253 { "suppress_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_attributes) },
254 { "visible_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , visible_attributes) },
255 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
256 { "suppress_operations", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_operations) },
257 { "visible_operations", PROP_TYPE_BOOL, offsetof(UMLClass , visible_operations) },
258 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
259 { "wrap_operations", PROP_TYPE_BOOL, offsetof(UMLClass , wrap_operations) },
260 { "wrap_after_char", PROP_TYPE_INT, offsetof(UMLClass , wrap_after_char) },
261 { "Comment_line_length", PROP_TYPE_INT, offsetof(UMLClass, Comment_line_length) },
263 /* all this just to make the defaults selectable ... */
264 PROP_OFFSET_STD_MULTICOL_BEGIN,
265 PROP_OFFSET_MULTICOL_COLUMN("font"),
266 { "normal_font", PROP_TYPE_FONT, offsetof(UMLClass, normal_font) },
267 { "abstract_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_font) },
268 { "polymorphic_font", PROP_TYPE_FONT, offsetof(UMLClass, polymorphic_font) },
269 { "classname_font", PROP_TYPE_FONT, offsetof(UMLClass, classname_font) },
270 { "abstract_classname_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_classname_font) },
271 { "comment_font", PROP_TYPE_FONT, offsetof(UMLClass, comment_font) },
273 PROP_OFFSET_MULTICOL_COLUMN("height"),
274 { "normal_font_height", PROP_TYPE_REAL, offsetof(UMLClass, font_height) },
275 { "abstract_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_font_height) },
276 { "polymorphic_font_height", PROP_TYPE_REAL, offsetof(UMLClass, polymorphic_font_height) },
277 { "classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, classname_font_height) },
278 { "abstract_classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_classname_font_height) },
279 { "comment_font_height", PROP_TYPE_REAL, offsetof(UMLClass, comment_font_height) },
280 PROP_OFFSET_STD_MULTICOL_END,
282 { "operations", PROP_TYPE_DARRAY, offsetof(UMLClass , operations) },
283 { "attributes", PROP_TYPE_DARRAY, offsetof(UMLClass , attributes) } ,
284 { "templates", PROP_TYPE_DARRAY, offsetof(UMLClass , formal_params) } ,
286 { NULL, 0, 0 },
289 static void
290 umlclass_get_props(UMLClass * umlclass, GPtrArray *props)
292 object_get_props_from_offsets(&umlclass->element.object,
293 umlclass_offsets, props);
296 static DiaMenuItem umlclass_menu_items[] = {
297 { N_("Show Comments"), umlclass_show_comments_callback, NULL,
298 DIAMENU_ACTIVE|DIAMENU_TOGGLE },
301 static DiaMenu umlclass_menu = {
302 N_("Class"),
303 sizeof(umlclass_menu_items)/sizeof(DiaMenuItem),
304 umlclass_menu_items,
305 NULL
308 DiaMenu *
309 umlclass_object_menu(DiaObject *obj, Point *p)
311 umlclass_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
312 (((UMLClass *)obj)->visible_comments?DIAMENU_TOGGLE_ON:0);
314 return &umlclass_menu;
317 ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data)
319 ObjectChange *change = new_object_state_change(obj, NULL, NULL, NULL );
321 ((UMLClass *)obj)->visible_comments = !((UMLClass *)obj)->visible_comments;
322 umlclass_calculate_data((UMLClass *)obj);
323 umlclass_update_data((UMLClass *)obj);
324 return change;
327 static void
328 umlclass_set_props(UMLClass *umlclass, GPtrArray *props)
330 /* now that operations/attributes can be set here as well we need to
331 * take for the number of connections update as well
332 * Note that due to a hack in umlclass_load, this is called before
333 * the normal connection points are set up.
335 DiaObject *obj = &umlclass->element.object;
336 GList *list;
337 int num;
339 object_set_props_from_offsets(&umlclass->element.object, umlclass_offsets,
340 props);
342 num = UMLCLASS_CONNECTIONPOINTS + umlclass_num_dynamic_connectionpoints(umlclass);
344 #ifdef UML_MAINPOINT
345 obj->num_connections = num + 1;
346 #else
347 obj->num_connections = num;
348 #endif
350 obj->connections = g_realloc(obj->connections, obj->num_connections*sizeof(ConnectionPoint *));
352 /* Update data: */
353 if (num > UMLCLASS_CONNECTIONPOINTS) {
354 int i;
355 /* this is just updating pointers to ConnectionPoint, the real connection handling is elsewhere.
356 * Note: Can't optimize here on number change cause the ops/attribs may have changed regardless of that.
358 i = UMLCLASS_CONNECTIONPOINTS;
359 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
360 while (list != NULL) {
361 UMLAttribute *attr = (UMLAttribute *)list->data;
363 printf("Setting obj conn %d to %p->left: %p\n", i, attr, attr->left_connection);
364 obj->connections[i] = attr->left_connection;
365 obj->connections[i]->object = obj;
366 i++;
367 printf("Setting obj conn %d to %p->right: %p\n", i, attr, attr->right_connection);
368 obj->connections[i] = attr->right_connection;
369 obj->connections[i]->object = obj;
370 i++;
371 list = g_list_next(list);
373 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
374 while (list != NULL) {
375 UMLOperation *op = (UMLOperation *)list->data;
376 obj->connections[i] = op->left_connection;
377 obj->connections[i]->object = obj;
378 i++;
379 obj->connections[i] = op->right_connection;
380 obj->connections[i]->object = obj;
381 i++;
382 list = g_list_next(list);
385 #ifdef UML_MAINPOINT
386 obj->connections[num] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
387 obj->connections[num]->object = obj;
388 #endif
390 umlclass_calculate_data(umlclass);
391 umlclass_update_data(umlclass);
392 /* Would like to sanity check here, but the call to object_load_props
393 * in umlclass_load means we will be called with inconsistent data. */
394 umlclass_sanity_check(umlclass, "After updating data");
397 static real
398 umlclass_distance_from(UMLClass *umlclass, Point *point)
400 DiaObject *obj = &umlclass->element.object;
401 return distance_rectangle_point(&obj->bounding_box, point);
404 static void
405 umlclass_select(UMLClass *umlclass, Point *clicked_point,
406 DiaRenderer *interactive_renderer)
408 element_update_handles(&umlclass->element);
411 static ObjectChange*
412 umlclass_move_handle(UMLClass *umlclass, Handle *handle,
413 Point *to, ConnectionPoint *cp,
414 HandleMoveReason reason, ModifierKeys modifiers)
416 assert(umlclass!=NULL);
417 assert(handle!=NULL);
418 assert(to!=NULL);
420 assert(handle->id < UMLCLASS_CONNECTIONPOINTS);
422 return NULL;
425 static ObjectChange*
426 umlclass_move(UMLClass *umlclass, Point *to)
428 umlclass->element.corner = *to;
429 umlclass_update_data(umlclass);
431 return NULL;
434 * underlines the text at the start point using the text to determine
435 * the length of the underline. Draw a line under the text represented by
436 * string using the selected renderer, color, and line width. Since
437 * drawing this line will change the line width used by DIA, the current
438 * line width that DIA is using is also passed so it can be restored once
439 * the line has been drawn.
441 * @param renderer the renderer that will draw the line
442 * @param StartPoint the start of the line to be drawn
443 * @param font the font used to draw the text being underlined
444 * @param font_height the size in the y direction of the font used to draw the text
445 * @param string the text string that is to be underlined
446 * @param color the color of the line to draw
447 * @param line_width default line thickness
448 * @param underline_width the thickness of the line to draw
452 static void
453 uml_underline_text(DiaRenderer *renderer,
454 Point StartPoint,
455 DiaFont *font,
456 real font_height,
457 gchar *string,
458 Color *color,
459 real line_width,
460 real underline_width)
462 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
463 Point UnderlineStartPoint;
464 Point UnderlineEndPoint;
466 UnderlineStartPoint = StartPoint;
467 UnderlineStartPoint.y += font_height * 0.1;
468 UnderlineEndPoint = UnderlineStartPoint;
469 UnderlineEndPoint.x += dia_font_string_width(string, font, font_height);
470 renderer_ops->set_linewidth(renderer, underline_width);
471 renderer_ops->draw_line(renderer, &UnderlineStartPoint, &UnderlineEndPoint, color);
472 renderer_ops->set_linewidth(renderer, line_width);
476 * Create a documentation tag from a comment.
478 * First a string is created containing only the text
479 * "{documentation = ". Then the contents of the comment string
480 * are added but wrapped. This is done by first looking for any
481 * New Line characters. If the line segment is longer than the
482 * WrapPoint would allow, the line is broken at either the
483 * first whitespace before the WrapPoint or if there are no
484 * whitespaces in the segment, at the WrapPoint. This
485 * continues until the entire string has been processed and
486 * then the resulting new string is returned. No attempt is
487 * made to rejoin any of the segments, that is all New Lines
488 * are treated as hard newlines. No syllable matching is done
489 * either so breaks in words will sometimes not make real
490 * sense.
491 * <p>
492 * Finally, since this function returns newly created dynamic
493 * memory the caller must free the memory to prevent memory
494 * leaks.
496 * @param comment The comment to be wrapped to the line length limit
497 * @param WrapPoint The maximum line length allowed for the line.
498 * @param NumberOfLines The number of comment lines after the wrapping.
499 * @return a pointer to the wrapped documentation
501 * NOTE:
502 * This function should most likely be move to a source file for
503 * handling global UML functionallity at some point.
505 static gchar *
506 uml_create_documentation_tag(gchar * comment,gint WrapPoint, gint *NumberOfLines)
508 gchar *CommentTag = "{documentation = ";
509 gint TagLength = strlen(CommentTag);
510 gchar *WrappedComment = g_malloc(TagLength+1);
511 gint LengthOfComment = strlen(comment);
512 gint CommentIndex = 0;
513 gint LengthOfWrappedComment= 0;
514 gint LineLen = WrapPoint - TagLength;
516 WrappedComment[0] = '\0';
517 strcat(WrappedComment, CommentTag);
518 LengthOfWrappedComment = strlen(WrappedComment);
519 *NumberOfLines = 1;
521 /* Remove leading whitespace */
522 while( isspace(comment[CommentIndex])){
523 CommentIndex++;
527 while( CommentIndex < LengthOfComment) /* more of the comment to go? */
529 gchar *Nl = strchr(&comment[CommentIndex], '\n');
530 gint BytesToNextNewLine = 0;
532 /* if this is the first line then we have to take into
533 * account the tag of the tagged value
536 LengthOfWrappedComment = strlen(WrappedComment);
538 * First handle the next new lines
540 if ( Nl != NULL){
541 BytesToNextNewLine = (Nl - &comment[CommentIndex]);
544 if ((Nl != NULL) && (BytesToNextNewLine < LineLen)){
545 LineLen = BytesToNextNewLine;
547 else{
548 if( (CommentIndex + LineLen) > LengthOfComment){
549 LineLen = LengthOfComment-CommentIndex;
551 while (LineLen > 0){
552 if ((LineLen == strlen(&comment[CommentIndex])) ||
553 isspace(comment[CommentIndex+LineLen])){
554 break;
555 } else{
556 LineLen--;
559 if ((*NumberOfLines > 1) &&( LineLen == 0)){
560 LineLen = WrapPoint;
563 if (LineLen < 0){
564 LineLen = 0;
567 /* Grow the wrapped text to make room for the NL and the next chunk */
568 WrappedComment = g_realloc(WrappedComment,LengthOfWrappedComment+LineLen+2);
569 memset(&WrappedComment[LengthOfWrappedComment],0,LineLen+2);
570 strncat(WrappedComment, &comment[CommentIndex], LineLen);
572 CommentIndex += LineLen;
573 while( isspace(comment[CommentIndex])){
574 CommentIndex++;
576 if (CommentIndex < LengthOfComment){
577 /* if this is not the last line add a new-line*/
578 strcat(WrappedComment,"\n");
579 *NumberOfLines+=1;
581 LengthOfWrappedComment = strlen(WrappedComment);
582 LineLen = WrapPoint;
584 WrappedComment = g_realloc(WrappedComment,LengthOfWrappedComment+2);
585 strcat(WrappedComment, "}");
586 return WrappedComment;
590 * Draw the comment at the point, p, using the comment font from the
591 * class defined by umlclass. When complete update the point to reflect
592 * the size of data drawn.
593 * The comment will have been word wrapped using the function
594 * uml_create_documentation_tag, so it may have more than one line on the
595 * display.
597 * @param renderer The Renderer on which the comment is being drawn
598 * @param font The font to render the comment in.
599 * @param font_height The Y size of the font used to render the comment
600 * @param text_color A pointer to the color to use to render the comment
601 * @param comment The comment string to render
602 * @param Comment_line_length The maximum length of any one line in the comment
603 * @param p The point at which the comment is to start
604 * @param alignment The method to use for alignment of the font
605 * @see uml_create_documentation
608 static void
609 uml_draw_comments(DiaRenderer *renderer,
610 DiaFont *font,
611 real font_height,
612 Color *text_color,
613 gchar *comment,
614 gint Comment_line_length,
615 Point *p,
616 gint alignment)
618 gint NumberOfLines = 0;
619 gint Index;
620 gchar *CommentString = 0;
621 gchar *NewLineP= NULL;
622 gchar *RenderP;
624 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
626 CommentString =
627 uml_create_documentation_tag(comment, Comment_line_length, &NumberOfLines);
628 RenderP = CommentString;
629 renderer_ops->set_font(renderer, font, font_height);
630 for ( Index=0; Index < NumberOfLines; Index++)
632 p->y += font_height; /* Advance to the next line */
633 NewLineP = strchr(RenderP, '\n');
634 if ( NewLineP != NULL)
636 *NewLineP++ = '\0';
638 renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
639 RenderP = NewLineP;
640 if ( NewLineP == NULL){
641 break;
644 g_free(CommentString);
649 * Draw the name box of the class icon. According to the UML specification,
650 * the Name box or compartment is the top most compartment of the class
651 * icon. It may contain one or more stereotype declarations, followed by
652 * the name of the class. The name may be rendered to indicate abstraction
653 * for abstract classes. Following the name is any tagged values such as
654 * the {documentation = } tag.
655 * <p>
656 * Because the start point is the upper left of the class box, templates
657 * tend to get lost when created. By applying an offset, they will not be
658 * lost. The offset should only be added if the elem->corner.y = 0.
660 * @param umlclass The pointer to the class being drawn
661 * @param renderer The pointer to the rendering object used to draw
662 * @param elem The pointer to the element within the class to be drawn
663 * @param offset offset from start point
664 * @return The offset from the start of the class to the bottom of the namebox
667 static real
668 umlclass_draw_namebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem )
670 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
671 real font_height;
672 DiaFont *font;
673 Point StartPoint;
674 Point LowerRightPoint;
675 real Yoffset;
676 Color *text_color = &umlclass->text_color;
680 StartPoint.x = elem->corner.x;
681 StartPoint.y = elem->corner.y;
683 Yoffset = elem->corner.y + umlclass->namebox_height;
685 LowerRightPoint = StartPoint;
686 LowerRightPoint.x += elem->width;
687 LowerRightPoint.y = Yoffset;
690 * First draw the outer box and fill color for the class name
691 * object
693 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->fill_color);
694 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->line_color);
696 /* Start at the midpoint on the X axis */
697 StartPoint.x += elem->width / 2.0;
699 /* stereotype: */
700 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
701 gchar *String = umlclass->stereotype_string;
702 StartPoint.y += 0.1;
703 StartPoint.y += dia_font_ascent(String, umlclass->normal_font, umlclass->font_height);
704 renderer_ops->set_font(renderer, umlclass->normal_font, umlclass->font_height);
705 renderer_ops->draw_string(renderer, String, &StartPoint, ALIGN_CENTER, text_color);
708 /* name: */
709 if (umlclass->name != NULL) {
710 if (umlclass->abstract) {
711 font = umlclass->abstract_classname_font;
712 font_height = umlclass->abstract_classname_font_height;
713 } else {
714 font = umlclass->classname_font;
715 font_height = umlclass->classname_font_height;
717 StartPoint.y += font_height;
719 renderer_ops->set_font(renderer, font, font_height);
720 renderer_ops->draw_string(renderer, umlclass->name, &StartPoint, ALIGN_CENTER, text_color);
723 /* comment */
724 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0'){
725 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
726 &umlclass->text_color, umlclass->comment,
727 umlclass->Comment_line_length, &StartPoint, ALIGN_CENTER);
729 return Yoffset;
732 /**
733 * Draw the attribute box.
734 * This attribute box follows the name box in the class icon. If the
735 * attributes are not suppress, draw each of the attributes following the
736 * UML specification for displaying attributes. Each attribute is preceded
737 * by the visibility character, +, - or # depending on whether it is public
738 * private or protected. If the attribute is "abstract" it will be rendered
739 * using the abstract font otherwise it will be rendered using the normal
740 * font. If the attribute is of class scope, static in C++, then it will be
741 * underlined. If there is a comment associated with the attribute, that is
742 * within the class description, it will be rendered as a uml comment.
744 * @param umlclass The pointer to the class being drawn
745 * @param renderer The pointer to the rendering object used to draw
746 * @param elem The pointer to the element within the class to be drawn
747 * @param Yoffset The Y offset from the start of the class at which to draw the attributebox
748 * @return The offset from the start of the class to the bottom of the attributebox
749 * @see uml_draw_comments
751 static real
752 umlclass_draw_attributebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
754 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
755 real font_height;
756 Point StartPoint;
757 Point LowerRight;
758 DiaFont *font;
759 Color *fill_color = &umlclass->fill_color;
760 Color *line_color = &umlclass->line_color;
761 Color *text_color = &umlclass->text_color;
762 GList *list;
764 StartPoint.x = elem->corner.x;
765 StartPoint.y = Yoffset;
766 Yoffset += umlclass->attributesbox_height;
768 LowerRight = StartPoint;
769 LowerRight.x += elem->width;
770 LowerRight.y = Yoffset;
772 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
773 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
775 if (!umlclass->suppress_attributes) {
776 gint i = 0;
777 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
778 StartPoint.y += 0.1;
780 list = umlclass->attributes;
781 while (list != NULL)
783 UMLAttribute *attr = (UMLAttribute *)list->data;
784 gchar *attstr = g_list_nth(umlclass->attributes_strings, i)->data;
786 if (attr->abstract) {
787 font = umlclass->abstract_font;
788 font_height = umlclass->abstract_font_height;
790 else {
791 font = umlclass->normal_font;
792 font_height = umlclass->font_height;
794 StartPoint.y += font_height;
795 renderer_ops->set_font (renderer, font, font_height);
796 renderer_ops->draw_string(renderer, attstr, &StartPoint, ALIGN_LEFT, text_color);
798 if (attr->class_scope) {
799 uml_underline_text(renderer, StartPoint, font, font_height, attstr, line_color,
800 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
803 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
804 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
805 &umlclass->text_color, attr->comment,
806 umlclass->Comment_line_length, &StartPoint, ALIGN_LEFT);
807 StartPoint.y += umlclass->comment_font_height/2;
809 list = g_list_next(list);
810 i++;
813 return Yoffset;
818 * Draw the operations box. The operations block follows the attribute box
819 * if it is visible. If the operations are not suppressed, they are
820 * displayed in the operations box. Like the attributes, operations have
821 * visibility characters, +,-, and # indicating whether the are public,
822 * private or protected. The operations are rendered in different fonts
823 * depending on whether they are abstract (pure virtual), polymorphic
824 * (virtual) or leaf (final virtual or non-virtual). The parameters to the
825 * operation may be displayed and if they are they may be conditionally
826 * wrapped to reduce horizontial size of the icon.
828 * @param umlclass The pointer to the class being drawn
829 * @param renderer The pointer to the rendering object used to draw
830 * @param elem The pointer to the element within the class to be drawn
831 * @param Yoffset The Y offset from the start of the class at which to draw the operationbox
832 * @return The offset from the start of the class to the bottom of the operationbox
835 static real
836 umlclass_draw_operationbox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
838 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
839 real font_height;
840 Point StartPoint;
841 Point LowerRight;
842 DiaFont *font;
843 GList *list;
844 Color *fill_color = &umlclass->fill_color;
845 Color *line_color = &umlclass->line_color;
846 Color *text_color = &umlclass->text_color;
849 StartPoint.x = elem->corner.x;
850 StartPoint.y = Yoffset;
851 Yoffset += umlclass->operationsbox_height;
853 LowerRight = StartPoint;
854 LowerRight.x += elem->width;
855 LowerRight.y = Yoffset;
857 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
858 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
860 if (!umlclass->suppress_operations) {
861 gint i = 0;
862 GList *wrapsublist = NULL;
863 gchar *part_opstr = NULL;
864 int wrap_pos, last_wrap_pos, ident, wrapping_needed;
865 int part_opstr_len = 0, part_opstr_need = 0;
867 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
868 StartPoint.y += 0.1;
870 list = umlclass->operations;
871 while (list != NULL) {
872 UMLOperation *op = (UMLOperation *)list->data;
873 gchar* opstr;
874 real ascent;
876 switch (op->inheritance_type) {
877 case UML_ABSTRACT:
878 font = umlclass->abstract_font;
879 font_height = umlclass->abstract_font_height;
880 break;
881 case UML_POLYMORPHIC:
882 font = umlclass->polymorphic_font;
883 font_height = umlclass->polymorphic_font_height;
884 break;
885 case UML_LEAF:
886 default:
887 font = umlclass->normal_font;
888 font_height = umlclass->font_height;
891 wrapping_needed = 0;
892 opstr = (gchar*) g_list_nth(umlclass->operations_strings, i)->data;
893 if( umlclass->wrap_operations ) {
894 wrapsublist = (GList*)g_list_nth( umlclass->operations_wrappos, i)->data;
895 wrapping_needed = GPOINTER_TO_INT( wrapsublist->data );
898 ascent = dia_font_ascent(opstr, font, font_height);
899 renderer_ops->set_font(renderer, font, font_height);
901 if( umlclass->wrap_operations && wrapping_needed) {
903 wrapsublist = g_list_next( wrapsublist);
904 ident = GPOINTER_TO_INT( wrapsublist->data);
905 wrapsublist = g_list_next( wrapsublist);
906 wrap_pos = last_wrap_pos = 0;
908 while( wrapsublist != NULL) {
909 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
911 if( last_wrap_pos == 0) {
912 part_opstr_need = wrap_pos + 1;
913 if (part_opstr_len < part_opstr_need) {
914 part_opstr_len = part_opstr_need;
915 part_opstr = g_realloc (part_opstr, part_opstr_need);
917 strncpy( part_opstr, opstr, wrap_pos);
918 memset( part_opstr+wrap_pos, '\0', 1);
920 else {
921 part_opstr_need = ident + wrap_pos - last_wrap_pos + 1;
922 if (part_opstr_len < part_opstr_need) {
923 part_opstr_len = part_opstr_need;
924 part_opstr = g_realloc (part_opstr, part_opstr_need);
926 memset( part_opstr, ' ', ident);
927 memset( part_opstr+ident, '\0', 1);
928 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
931 StartPoint.y += ascent;
932 renderer_ops->draw_string(renderer, part_opstr, &StartPoint, ALIGN_LEFT, text_color);
933 last_wrap_pos = wrap_pos;
934 wrapsublist = g_list_next( wrapsublist);
937 else
939 StartPoint.y += ascent;
940 renderer_ops->draw_string(renderer, opstr, &StartPoint, ALIGN_LEFT, text_color);
943 if (op->class_scope) {
944 uml_underline_text(renderer, StartPoint, font, font_height, opstr, line_color,
945 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
948 StartPoint.y += font_height - ascent;
950 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
951 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
952 &umlclass->text_color, op->comment,
953 umlclass->Comment_line_length, &StartPoint, ALIGN_LEFT);
956 list = g_list_next(list);
957 i++;
959 if (part_opstr){
960 g_free(part_opstr);
963 return Yoffset;
967 * Draw the template parameters box in the upper right hand corner of the
968 * class box for paramertize classes (aka template classes). Fill in this
969 * box with the parameters for the class.
970 * <p>
971 * At this time there is no provision for adding comments or documentation
972 * to the display.
974 * @param umlclass The pointer to the class being drawn
975 * @param renderer The pointer to the rendering object used to draw
976 * @param elem The pointer to the element within the class to be drawn
979 static void
980 umlclass_draw_template_parameters_box(UMLClass *umlclass, DiaRenderer *renderer, Element *elem)
982 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
983 Point UpperLeft;
984 Point LowerRight;
985 Point TextInsert;
986 GList *list;
987 gint i;
988 DiaFont *font = umlclass->normal_font;
989 real font_height = umlclass->font_height;
990 Color *fill_color = &umlclass->fill_color;
991 Color *line_color = &umlclass->line_color;
992 Color *text_color = &umlclass->text_color;
996 * Adjust for the overlay of the template on the class icon
998 UpperLeft.x = elem->corner.x + elem->width - UMLCLASS_TEMPLATE_OVERLAY_X;
999 UpperLeft.y = elem->corner.y - umlclass->templates_height + UMLCLASS_TEMPLATE_OVERLAY_Y;
1000 TextInsert = UpperLeft;
1001 LowerRight = UpperLeft;
1002 LowerRight.x += umlclass->templates_width;
1003 LowerRight.y += umlclass->templates_height;
1005 renderer_ops->fill_rect(renderer, &UpperLeft, &LowerRight, fill_color);
1006 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
1007 renderer_ops->set_dashlength(renderer, 0.3);
1008 renderer_ops->draw_rect(renderer, &UpperLeft, &LowerRight, line_color);
1010 TextInsert.x += 0.3;
1011 renderer_ops->set_font(renderer, font, font_height);
1012 i = 0;
1013 list = umlclass->formal_params;
1014 while (list != NULL)
1016 gchar *ParameterString = umlclass->templates_strings[i];
1018 TextInsert.y +=(0.1 + dia_font_ascent(ParameterString, font, font_height));
1019 renderer_ops->draw_string(renderer, ParameterString, &TextInsert, ALIGN_LEFT, text_color);
1021 list = g_list_next(list);
1022 i++;
1027 * Draw the class icon for the specified UMLClass object.
1028 * Set the renderer to the correct fill and line styles and the appropriate
1029 * line width. The object is drawn by the umlclass_draw_namebox,
1030 * umlclass_draw_attributebox, umlclass_draw_operationbox and
1031 * umlclass_draw_template_parameters_box.
1033 * @param umlclass object based on the uml class that is being rendered
1034 * @param DiaRenderer renderer used to draw the object
1038 static void
1039 umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer)
1041 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1042 real y = 0.0;
1043 Element *elem;
1045 assert(umlclass != NULL);
1046 assert(renderer != NULL);
1048 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
1049 renderer_ops->set_linewidth(renderer, UMLCLASS_BORDER);
1050 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
1052 elem = &umlclass->element;
1054 y = umlclass_draw_namebox(umlclass, renderer, elem);
1055 if (umlclass->visible_attributes) {
1056 y = umlclass_draw_attributebox(umlclass, renderer, elem, y);
1058 if (umlclass->visible_operations) {
1059 y = umlclass_draw_operationbox(umlclass, renderer, elem, y);
1061 if (umlclass->template) {
1062 umlclass_draw_template_parameters_box(umlclass, renderer, elem);
1066 void
1067 umlclass_update_data(UMLClass *umlclass)
1069 Element *elem = &umlclass->element;
1070 DiaObject *obj = &elem->object;
1071 real x,y;
1072 GList *list;
1073 int i;
1074 int pointswide;
1075 int lowerleftcorner;
1076 real pointspacing;
1078 x = elem->corner.x;
1079 y = elem->corner.y;
1081 /* Update connections: */
1082 umlclass->connections[0].pos = elem->corner;
1083 umlclass->connections[0].directions = DIR_NORTH|DIR_WEST;
1085 /* there are four corner points and two side points, thus all
1086 * remaining points are on the top/bottom width
1088 pointswide = (UMLCLASS_CONNECTIONPOINTS - 6) / 2;
1089 pointspacing = elem->width / (pointswide + 1.0);
1091 /* across the top connection points */
1092 for (i=1;i<=pointswide;i++) {
1093 umlclass->connections[i].pos.x = x + (pointspacing * i);
1094 umlclass->connections[i].pos.y = y;
1095 umlclass->connections[i].directions = DIR_NORTH;
1098 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 2;
1099 umlclass->connections[i].pos.x = x + elem->width;
1100 umlclass->connections[i].pos.y = y;
1101 umlclass->connections[i].directions = DIR_NORTH|DIR_EAST;
1103 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 1;
1104 umlclass->connections[i].pos.x = x;
1105 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1106 umlclass->connections[i].directions = DIR_WEST;
1108 i = (UMLCLASS_CONNECTIONPOINTS / 2);
1109 umlclass->connections[i].pos.x = x + elem->width;
1110 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1111 umlclass->connections[i].directions = DIR_EAST;
1113 i = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1114 umlclass->connections[i].pos.x = x;
1115 umlclass->connections[i].pos.y = y + elem->height;
1116 umlclass->connections[i].directions = DIR_WEST|DIR_SOUTH;
1118 /* across the bottom connection points */
1119 lowerleftcorner = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1120 for (i=1;i<=pointswide;i++) {
1121 umlclass->connections[lowerleftcorner + i].pos.x = x + (pointspacing * i);
1122 umlclass->connections[lowerleftcorner + i].pos.y = y + elem->height;
1123 umlclass->connections[lowerleftcorner + i].directions = DIR_SOUTH;
1126 /* bottom-right corner */
1127 i = (UMLCLASS_CONNECTIONPOINTS) - 1;
1128 umlclass->connections[i].pos.x = x + elem->width;
1129 umlclass->connections[i].pos.y = y + elem->height;
1130 umlclass->connections[i].directions = DIR_EAST|DIR_SOUTH;
1132 #ifdef UML_MAINPOINT
1133 /* Main point -- lives just after fixed connpoints in umlclass array */
1134 i = UMLCLASS_CONNECTIONPOINTS;
1135 umlclass->connections[i].pos.x = x + elem->width / 2;
1136 umlclass->connections[i].pos.y = y + elem->height / 2;
1137 umlclass->connections[i].directions = DIR_ALL;
1138 umlclass->connections[i].flags = CP_FLAGS_MAIN;
1139 #endif
1141 y += umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1143 list = umlclass->attributes;
1144 while (list != NULL) {
1145 UMLAttribute *attr = (UMLAttribute *)list->data;
1147 attr->left_connection->pos.x = x;
1148 attr->left_connection->pos.y = y;
1149 attr->left_connection->directions = DIR_WEST;
1150 attr->right_connection->pos.x = x + elem->width;
1151 attr->right_connection->pos.y = y;
1152 attr->right_connection->directions = DIR_EAST;
1154 y += umlclass->font_height;
1155 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1156 y += umlclass->comment_font_height;
1158 list = g_list_next(list);
1161 y = elem->corner.y + umlclass->namebox_height +
1162 umlclass->attributesbox_height + 0.1 + umlclass->font_height/2;
1164 list = umlclass->operations;
1165 while (list != NULL) {
1166 UMLOperation *op = (UMLOperation *)list->data;
1168 op->left_connection->pos.x = x;
1169 op->left_connection->pos.y = y;
1170 op->left_connection->directions = DIR_WEST;
1171 op->right_connection->pos.x = x + elem->width;
1172 op->right_connection->pos.y = y;
1173 op->right_connection->directions = DIR_EAST;
1175 y += umlclass->font_height;
1176 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0')
1177 y += umlclass->comment_font_height;
1179 list = g_list_next(list);
1182 element_update_boundingbox(elem);
1184 if (umlclass->template) {
1185 /* fix boundingumlclass for templates: */
1186 obj->bounding_box.top -= (umlclass->templates_height - 0.3) ;
1187 obj->bounding_box.right += (umlclass->templates_width - 2.3);
1190 obj->position = elem->corner;
1192 element_update_handles(elem);
1194 umlclass_sanity_check(umlclass, "After updating data");
1198 * Calculate the dimensions of the class icons namebox for a given object of UMLClass.
1199 * The height is stored in the class structure. When calculating the
1200 * comment, if any, the comment is word wrapped and the resulting number of
1201 * lines is then used to calculate the height of the bounding box.
1203 * @param umlclass pointer to the object of UMLClass to calculate
1204 * @return the horizontal size of the name box.
1208 static real
1209 umlclass_calculate_name_data(UMLClass *umlclass)
1211 real maxwidth = 0.0;
1212 real width = 0.0;
1213 /* name box: */
1215 if (umlclass->name != NULL && umlclass->name[0] != '\0') {
1216 if (umlclass->abstract) {
1217 maxwidth = dia_font_string_width(umlclass->name,
1218 umlclass->abstract_classname_font,
1219 umlclass->abstract_classname_font_height);
1220 } else {
1221 maxwidth = dia_font_string_width(umlclass->name,
1222 umlclass->classname_font,
1223 umlclass->classname_font_height);
1227 umlclass->namebox_height = umlclass->classname_font_height + 4*0.1;
1228 if (umlclass->stereotype_string != NULL) {
1229 g_free(umlclass->stereotype_string);
1231 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
1232 umlclass->namebox_height += umlclass->font_height;
1233 umlclass->stereotype_string = g_strconcat ( UML_STEREOTYPE_START,
1234 umlclass->stereotype,
1235 UML_STEREOTYPE_END,
1236 NULL);
1238 width = dia_font_string_width (umlclass->stereotype_string,
1239 umlclass->normal_font,
1240 umlclass->font_height);
1241 maxwidth = MAX(width, maxwidth);
1242 } else {
1243 umlclass->stereotype_string = NULL;
1246 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0')
1248 int NumberOfCommentLines = 0;
1249 gchar *wrapped_box = uml_create_documentation_tag(umlclass->comment,
1250 umlclass->Comment_line_length,
1251 &NumberOfCommentLines);
1253 width = dia_font_string_width (wrapped_box,
1254 umlclass->comment_font,
1255 umlclass->comment_font_height);
1257 g_free(wrapped_box);
1258 umlclass->namebox_height += umlclass->comment_font_height * NumberOfCommentLines;
1259 maxwidth = MAX(width, maxwidth);
1261 return maxwidth;
1264 * Calculate the dimensions of the attribute box on an object of type UMLClass.
1265 * @param umlclass a pointer to an object of UMLClass
1266 * @return the horizontal size of the attribute box
1270 static real
1271 umlclass_calculate_attribute_data(UMLClass *umlclass)
1273 int i;
1274 int pos_next_comma;
1275 int pos_brace;
1276 int wrap_pos;
1277 int last_wrap_pos;
1278 int maxlinewidth;
1279 int length;
1280 real maxwidth = 0.0;
1281 real width = 0.0;
1282 GList *list;
1284 /* attributes box: */
1285 if (umlclass->attributes_strings != NULL)
1287 g_list_foreach(umlclass->attributes_strings, (GFunc)g_free, NULL);
1288 g_list_free(umlclass->attributes_strings);
1290 umlclass->attributesbox_height = 2*0.1;
1292 umlclass->attributes_strings = NULL;
1293 if (g_list_length(umlclass->attributes) != 0)
1295 i = 0;
1296 list = umlclass->attributes;
1297 while (list != NULL)
1299 UMLAttribute *attr = (UMLAttribute *) list->data;
1300 gchar *attstr = uml_get_attribute_string(attr);
1302 umlclass->attributes_strings =
1303 g_list_append(umlclass->attributes_strings, attstr);
1305 if (attr->abstract)
1307 width = dia_font_string_width(attstr,
1308 umlclass->abstract_font,
1309 umlclass->abstract_font_height);
1310 umlclass->attributesbox_height += umlclass->abstract_font_height;
1312 else
1314 width = dia_font_string_width(attstr,
1315 umlclass->normal_font,
1316 umlclass->font_height);
1317 umlclass->attributesbox_height += umlclass->font_height;
1319 maxwidth = MAX(width, maxwidth);
1321 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1323 int NumberOfLines = 0;
1324 gchar *Wrapped = uml_create_documentation_tag(attr->comment,
1325 umlclass->Comment_line_length,
1326 &NumberOfLines);
1328 width = dia_font_string_width(Wrapped,
1329 umlclass->comment_font,
1330 umlclass->comment_font_height);
1332 g_free(Wrapped);
1333 umlclass->attributesbox_height += (umlclass->comment_font_height * (NumberOfLines));
1334 umlclass->attributesbox_height += umlclass->comment_font_height/2;
1336 maxwidth = MAX(width, maxwidth);
1339 i++;
1340 list = g_list_next(list);
1344 if ((umlclass->attributesbox_height<0.4)|| umlclass->suppress_attributes )
1346 umlclass->attributesbox_height = 0.4;
1348 return maxwidth;
1352 * Calculate the dimensions of the operations box of an object of UMLClass.
1353 * The vertical size or height is stored in the object.
1354 * @param umlclass a pointer to an object of UMLClass
1355 * @return the horizontial size of the operations box
1359 static real
1360 umlclass_calculate_operation_data(UMLClass *umlclass)
1362 int i;
1363 int pos_next_comma;
1364 int pos_brace;
1365 int wrap_pos;
1366 int last_wrap_pos;
1367 int ident;
1368 int offset;
1369 int maxlinewidth;
1370 int length;
1371 real maxwidth = 0.0;
1372 real width = 0.0;
1373 GList *list;
1374 GList *sublist;
1375 GList *wrapsublist;
1377 /* operations box: */
1378 umlclass->operationsbox_height = 2*0.1;
1379 /* neither leak previously calculated strings ... */
1380 if (umlclass->operations_strings != NULL)
1382 g_list_foreach(umlclass->operations_strings, (GFunc)g_free, NULL);
1383 g_list_free(umlclass->operations_strings);
1384 umlclass->operations_strings = NULL;
1386 /* ... nor their wrappings */
1387 if (umlclass->operations_wrappos != NULL)
1389 g_list_foreach(umlclass->operations_wrappos, (GFunc)g_list_free, NULL);
1390 g_list_free(umlclass->operations_wrappos);
1391 umlclass->operations_wrappos = NULL;
1394 if (0 != g_list_length(umlclass->operations))
1396 i = 0;
1397 list = umlclass->operations;
1398 while (list != NULL)
1400 UMLOperation *op = (UMLOperation *) list->data;
1401 gchar *opstr = uml_get_operation_string(op);
1403 umlclass->operations_strings =
1404 g_list_append(umlclass->operations_strings, opstr);
1406 length = 0;
1407 if( umlclass->wrap_operations )
1409 length = strlen( (const gchar*)opstr);
1410 sublist = NULL;
1411 if( length > umlclass->wrap_after_char)
1413 gchar *part_opstr;
1414 sublist = g_list_append( sublist, GINT_TO_POINTER( 1));
1416 /* count maximal line width to create a secure buffer (part_opstr)
1417 and build the sublist with the wrapping data for the current operation, which will be used by umlclass_draw(), too.
1418 The content of the sublist is:
1419 1st element: (bool) wrapping needed or not, 2nd: indentation in chars, 3rd-last: absolute wrapping positions */
1420 pos_next_comma = pos_brace = wrap_pos = offset = maxlinewidth = umlclass->max_wrapped_line_width = 0;
1421 while( wrap_pos + offset < length)
1425 pos_next_comma = strcspn( (const gchar*)opstr + wrap_pos + offset, ",");
1426 wrap_pos += pos_next_comma + 1;
1427 } while( wrap_pos < umlclass->wrap_after_char - pos_brace && wrap_pos + offset < length);
1429 if( offset == 0){
1430 pos_brace = strcspn( opstr, "(");
1431 sublist = g_list_append( sublist, GINT_TO_POINTER( pos_brace+1));
1433 sublist = g_list_append( sublist, GINT_TO_POINTER( wrap_pos + offset));
1435 maxlinewidth = MAX(maxlinewidth, wrap_pos);
1437 offset += wrap_pos;
1438 wrap_pos = 0;
1440 umlclass->max_wrapped_line_width = MAX( umlclass->max_wrapped_line_width, maxlinewidth+1);
1442 wrapsublist = g_list_next( sublist);
1443 ident = GPOINTER_TO_INT( wrapsublist->data);
1444 part_opstr = g_alloca(umlclass->max_wrapped_line_width+ident+1);
1445 pos_next_comma = pos_brace = wrap_pos = offset = 0;
1447 wrapsublist = g_list_next( wrapsublist);
1448 wrap_pos = last_wrap_pos = 0;
1450 while( wrapsublist != NULL){
1451 DiaFont *Font;
1452 real FontHeight;
1453 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
1454 if( last_wrap_pos == 0){
1455 strncpy( part_opstr, opstr, wrap_pos);
1456 memset( part_opstr+wrap_pos, '\0', 1);
1458 else
1460 memset( part_opstr, ' ', ident);
1461 memset( part_opstr+ident, '\0', 1);
1462 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
1465 switch(op->inheritance_type)
1467 case UML_ABSTRACT:
1468 Font = umlclass->abstract_font;
1469 FontHeight = umlclass->abstract_font_height;
1470 break;
1471 case UML_POLYMORPHIC:
1472 Font = umlclass->polymorphic_font;
1473 FontHeight = umlclass->polymorphic_font_height;
1474 break;
1475 case UML_LEAF:
1476 default:
1477 Font = umlclass->normal_font;
1478 FontHeight = umlclass->font_height;
1480 width = dia_font_string_width(part_opstr,Font,FontHeight);
1481 umlclass->operationsbox_height += FontHeight;
1483 maxwidth = MAX(width, maxwidth);
1484 last_wrap_pos = wrap_pos;
1485 wrapsublist = g_list_next( wrapsublist);
1488 else
1490 sublist = g_list_append( sublist, GINT_TO_POINTER( 0));
1492 umlclass->operations_wrappos = g_list_append( umlclass->operations_wrappos, sublist);
1495 if( !umlclass->wrap_operations || !(length > umlclass->wrap_after_char)) {
1496 DiaFont *Font;
1497 real FontHeight;
1499 switch(op->inheritance_type)
1501 case UML_ABSTRACT:
1502 Font = umlclass->abstract_font;
1503 FontHeight = umlclass->abstract_font_height;
1504 break;
1505 case UML_POLYMORPHIC:
1506 Font = umlclass->polymorphic_font;
1507 FontHeight = umlclass->polymorphic_font_height;
1508 break;
1509 case UML_LEAF:
1510 default:
1511 Font = umlclass->normal_font;
1512 FontHeight = umlclass->font_height;
1514 width = dia_font_string_width(opstr,Font,FontHeight);
1515 umlclass->operationsbox_height += FontHeight;
1517 maxwidth = MAX(width, maxwidth);
1520 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1521 int NumberOfLines = 0;
1522 gchar *Wrapped = uml_create_documentation_tag(op->comment,
1523 umlclass->Comment_line_length,
1524 &NumberOfLines);
1526 width = dia_font_string_width(Wrapped,
1527 umlclass->comment_font,
1528 umlclass->comment_font_height);
1530 g_free(Wrapped);
1531 umlclass->operationsbox_height += (umlclass->comment_font_height * (NumberOfLines+1));
1533 maxwidth = MAX(width, maxwidth);
1536 i++;
1537 list = g_list_next(list);
1541 umlclass->element.width = maxwidth + 2*0.3;
1543 if ((umlclass->operationsbox_height<0.4) || umlclass->suppress_operations ) {
1544 umlclass->operationsbox_height = 0.4;
1547 return maxwidth;
1551 * calculate the size of the class icon for an object of UMLClass.
1552 * This is done by calculating the size of the text to be displayed within
1553 * each of the contained bounding boxes, name, attributes and operations.
1554 * Because the comments may require wrapping, each comment is wrapped and
1555 * the resulting number of lines is used to calculate the size of the
1556 * comment within the box. The various font settings with in the class
1557 * properties contribute to the overall size of the resulting bounding box.
1559 * * @param umlclass a pointer to an object of UMLClass
1562 void
1563 umlclass_calculate_data(UMLClass *umlclass)
1565 int i;
1566 int pos_next_comma;
1567 int pos_brace;
1568 int wrap_pos;
1569 int last_wrap_pos;
1570 int ident;
1571 int offset;
1572 int maxlinewidth;
1573 int length;
1574 real maxwidth = 0.0;
1575 real width;
1576 GList *list;
1577 GList *sublist;
1578 GList *wrapsublist;
1581 if (!umlclass->destroyed)
1583 maxwidth = MAX(umlclass_calculate_name_data(umlclass), maxwidth);
1585 umlclass->element.height = umlclass->namebox_height;
1587 if (umlclass->visible_attributes){
1588 maxwidth = MAX(umlclass_calculate_attribute_data(umlclass), maxwidth);
1589 umlclass->element.height += umlclass->attributesbox_height;
1591 if (umlclass->visible_operations){
1592 maxwidth = MAX(umlclass_calculate_operation_data(umlclass), maxwidth);
1593 umlclass->element.height += umlclass->operationsbox_height;
1595 umlclass->element.width = maxwidth+0.5;
1596 /* templates box: */
1597 if (umlclass->templates_strings != NULL)
1599 for (i=0;i<umlclass->num_templates;i++)
1601 g_free(umlclass->templates_strings[i]);
1603 g_free(umlclass->templates_strings);
1605 umlclass->num_templates = g_list_length(umlclass->formal_params);
1607 umlclass->templates_height =
1608 umlclass->font_height * umlclass->num_templates + 2*0.1;
1609 umlclass->templates_height = MAX(umlclass->templates_height, 0.4);
1612 umlclass->templates_strings = NULL;
1613 maxwidth = 2.3;
1614 if (umlclass->num_templates != 0)
1616 umlclass->templates_strings =
1617 g_malloc (sizeof (gchar *) * umlclass->num_templates);
1618 i = 0;
1619 list = umlclass->formal_params;
1620 while (list != NULL)
1622 UMLFormalParameter *param;
1624 param = (UMLFormalParameter *) list->data;
1625 umlclass->templates_strings[i] = uml_get_formalparameter_string(param);
1627 width = dia_font_string_width(umlclass->templates_strings[i],
1628 umlclass->normal_font,
1629 umlclass->font_height);
1630 maxwidth = MAX(width, maxwidth);
1632 i++;
1633 list = g_list_next(list);
1636 umlclass->templates_width = maxwidth + 2*0.2;
1640 static void
1641 fill_in_fontdata(UMLClass *umlclass)
1643 if (umlclass->normal_font == NULL) {
1644 umlclass->font_height = 0.8;
1645 umlclass->normal_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, 0.8);
1647 if (umlclass->abstract_font == NULL) {
1648 umlclass->abstract_font_height = 0.8;
1649 umlclass->abstract_font =
1650 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD, 0.8);
1652 if (umlclass->polymorphic_font == NULL) {
1653 umlclass->polymorphic_font_height = 0.8;
1654 umlclass->polymorphic_font =
1655 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC, 0.8);
1657 if (umlclass->classname_font == NULL) {
1658 umlclass->classname_font_height = 1.0;
1659 umlclass->classname_font =
1660 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD, 1.0);
1662 if (umlclass->abstract_classname_font == NULL) {
1663 umlclass->abstract_classname_font_height = 1.0;
1664 umlclass->abstract_classname_font =
1665 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC, 1.0);
1667 if (umlclass->comment_font == NULL) {
1668 umlclass->comment_font_height = 0.7;
1669 umlclass->comment_font = dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1673 * Create an object of type class
1674 * By default this will create a object of class UMLClass. Howerver there
1675 * are at least two types of UMLClass objects, so the user_data is selects
1676 * the correct UMLClass object. Other than that this is quite straight
1677 * forward. The key to the polymorphic nature of this object is the use of
1678 * the DiaObjectType record which in conjunction with the user_data
1679 * controls the specific derived object type.
1681 * @param startpoint the origin of the object being created
1682 * @param user_data Information used by this routine to create the appropriate object
1683 * @param handle1 ignored when creating a class object
1684 * @param handle2 ignored when creating a class object
1685 * @return a pointer to the object created
1687 * NOTE:
1688 * This function should most likely be move to a source file for
1689 * handling global UML functionallity at some point.
1692 static DiaObject *
1693 umlclass_create(Point *startpoint,
1694 void *user_data,
1695 Handle **handle1,
1696 Handle **handle2)
1698 UMLClass *umlclass;
1699 Element *elem;
1700 DiaObject *obj;
1701 int i;
1703 umlclass = g_malloc0(sizeof(UMLClass));
1704 elem = &umlclass->element;
1705 obj = &elem->object;
1708 elem->corner = *startpoint;
1710 #ifdef UML_MAINPOINT
1711 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1); /* No attribs or ops => 0 extra connectionpoints. */
1712 #else
1713 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS); /* No attribs or ops => 0 extra connectionpoints. */
1714 #endif
1716 umlclass->properties_dialog = NULL;
1717 fill_in_fontdata(umlclass);
1721 * The following block of code may need to be converted to a switch statement if more than
1722 * two types of objects can be made - Dave Klotzbach
1724 umlclass->template = (GPOINTER_TO_INT(user_data)==1);
1726 if (umlclass->template){
1727 umlclass->name = g_strdup (_("Template"));
1729 else {
1730 umlclass->name = g_strdup (_("Class"));
1732 obj->type = &umlclass_type;
1733 obj->ops = &umlclass_ops;
1735 umlclass->stereotype = NULL;
1736 umlclass->comment = NULL;
1738 umlclass->abstract = FALSE;
1740 umlclass->suppress_attributes = FALSE;
1741 umlclass->suppress_operations = FALSE;
1743 umlclass->visible_attributes = TRUE;
1744 umlclass->visible_operations = TRUE;
1745 umlclass->visible_comments = FALSE;
1747 umlclass->wrap_operations = TRUE;
1748 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
1750 umlclass->attributes = NULL;
1752 umlclass->operations = NULL;
1754 umlclass->formal_params = NULL;
1756 umlclass->stereotype_string = NULL;
1757 umlclass->attributes_strings = NULL;
1758 umlclass->operations_strings = NULL;
1759 umlclass->operations_wrappos = NULL;
1760 umlclass->templates_strings = NULL;
1762 umlclass->text_color = color_black;
1763 umlclass->line_color = attributes_get_foreground();
1764 umlclass->fill_color = attributes_get_background();
1766 umlclass_calculate_data(umlclass);
1768 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1769 obj->connections[i] = &umlclass->connections[i];
1770 umlclass->connections[i].object = obj;
1771 umlclass->connections[i].connected = NULL;
1773 #ifdef UML_MAINPOINT
1774 /* Put mainpoint at the end, after conditional attr/oprn points,
1775 * but store it in the local connectionpoint array. */
1776 i += umlclass_num_dynamic_connectionpoints(umlclass);
1777 obj->connections[i] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
1778 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = obj;
1779 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1780 #endif
1782 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
1783 umlclass_update_data(umlclass);
1785 for (i=0;i<8;i++) {
1786 obj->handles[i]->type = HANDLE_NON_MOVABLE;
1789 *handle1 = NULL;
1790 *handle2 = NULL;
1791 return &umlclass->element.object;
1794 static void
1795 umlclass_destroy(UMLClass *umlclass)
1797 int i;
1798 GList *list;
1799 UMLAttribute *attr;
1800 UMLOperation *op;
1801 UMLFormalParameter *param;
1803 umlclass_sanity_check(umlclass, "Destroying");
1805 umlclass->destroyed = TRUE;
1807 dia_font_unref(umlclass->normal_font);
1808 dia_font_unref(umlclass->abstract_font);
1809 dia_font_unref(umlclass->polymorphic_font);
1810 dia_font_unref(umlclass->classname_font);
1811 dia_font_unref(umlclass->abstract_classname_font);
1812 dia_font_unref(umlclass->comment_font);
1814 element_destroy(&umlclass->element);
1816 g_free(umlclass->name);
1817 g_free(umlclass->stereotype);
1818 g_free(umlclass->comment);
1820 list = umlclass->attributes;
1821 while (list != NULL) {
1822 attr = (UMLAttribute *)list->data;
1823 printf("Destroying attr %p\n", attr);
1824 uml_attribute_destroy(attr);
1825 list = g_list_next(list);
1827 printf("Freeing umlclass->attributes %p\n", umlclass->attributes);
1828 g_list_free(umlclass->attributes);
1830 list = umlclass->operations;
1831 while (list != NULL) {
1832 op = (UMLOperation *)list->data;
1833 uml_operation_destroy(op);
1834 list = g_list_next(list);
1836 g_list_free(umlclass->operations);
1838 list = umlclass->formal_params;
1839 while (list != NULL) {
1840 param = (UMLFormalParameter *)list->data;
1841 uml_formalparameter_destroy(param);
1842 list = g_list_next(list);
1844 g_list_free(umlclass->formal_params);
1846 if (umlclass->stereotype_string != NULL) {
1847 g_free(umlclass->stereotype_string);
1850 if (umlclass->attributes_strings != NULL) {
1851 g_list_foreach(umlclass->attributes_strings, (GFunc)g_free, NULL);
1852 g_list_free(umlclass->attributes_strings);
1853 umlclass->attributes_strings = NULL;
1856 if (umlclass->operations_strings != NULL) {
1857 g_list_foreach(umlclass->operations_strings, (GFunc)g_free, NULL);
1858 g_list_free(umlclass->operations_strings);
1859 umlclass->operations_strings = NULL;
1862 if (umlclass->operations_wrappos != NULL) {
1863 g_list_foreach(umlclass->operations_wrappos, (GFunc)g_list_free, NULL);
1864 g_list_free(umlclass->operations_wrappos);
1865 umlclass->operations_wrappos = NULL;
1868 if (umlclass->templates_strings != NULL) {
1869 for (i=0;i<umlclass->num_templates;i++) {
1870 g_free(umlclass->templates_strings[i]);
1872 g_free(umlclass->templates_strings);
1875 if (umlclass->properties_dialog != NULL) {
1876 g_list_free(umlclass->properties_dialog->deleted_connections);
1877 gtk_widget_destroy(umlclass->properties_dialog->dialog);
1878 g_free(umlclass->properties_dialog);
1882 static DiaObject *
1883 umlclass_copy(UMLClass *umlclass)
1885 int i;
1886 UMLClass *newumlclass;
1887 Element *elem, *newelem;
1888 DiaObject *newobj;
1889 GList *list;
1890 UMLAttribute *attr;
1891 UMLAttribute *newattr;
1892 UMLOperation *op;
1893 UMLOperation *newop;
1894 UMLFormalParameter *param;
1896 elem = &umlclass->element;
1898 newumlclass = g_malloc0(sizeof(UMLClass));
1899 newelem = &newumlclass->element;
1900 newobj = &newelem->object;
1902 element_copy(elem, newelem);
1904 newumlclass->font_height = umlclass->font_height;
1905 newumlclass->abstract_font_height = umlclass->abstract_font_height;
1906 newumlclass->polymorphic_font_height = umlclass->polymorphic_font_height;
1907 newumlclass->classname_font_height = umlclass->classname_font_height;
1908 newumlclass->abstract_classname_font_height =
1909 umlclass->abstract_classname_font_height;
1910 newumlclass->comment_font_height =
1911 umlclass->comment_font_height;
1913 newumlclass->normal_font =
1914 dia_font_ref(umlclass->normal_font);
1915 newumlclass->abstract_font =
1916 dia_font_ref(umlclass->abstract_font);
1917 newumlclass->polymorphic_font =
1918 dia_font_ref(umlclass->polymorphic_font);
1919 newumlclass->classname_font =
1920 dia_font_ref(umlclass->classname_font);
1921 newumlclass->abstract_classname_font =
1922 dia_font_ref(umlclass->abstract_classname_font);
1923 newumlclass->comment_font =
1924 dia_font_ref(umlclass->comment_font);
1926 newumlclass->name = g_strdup(umlclass->name);
1927 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0')
1928 newumlclass->stereotype = g_strdup(umlclass->stereotype);
1929 else
1930 newumlclass->stereotype = NULL;
1932 if (umlclass->comment != NULL)
1933 newumlclass->comment = g_strdup(umlclass->comment);
1934 else
1935 newumlclass->comment = NULL;
1937 newumlclass->abstract = umlclass->abstract;
1938 newumlclass->suppress_attributes = umlclass->suppress_attributes;
1939 newumlclass->suppress_operations = umlclass->suppress_operations;
1940 newumlclass->visible_attributes = umlclass->visible_attributes;
1941 newumlclass->visible_operations = umlclass->visible_operations;
1942 newumlclass->visible_comments = umlclass->visible_comments;
1943 newumlclass->wrap_operations = umlclass->wrap_operations;
1944 newumlclass->wrap_after_char = umlclass->wrap_after_char;
1945 newumlclass->Comment_line_length = umlclass->Comment_line_length;
1946 newumlclass->text_color = umlclass->text_color;
1947 newumlclass->line_color = umlclass->line_color;
1948 newumlclass->fill_color = umlclass->fill_color;
1950 newumlclass->attributes = NULL;
1951 list = umlclass->attributes;
1952 while (list != NULL) {
1953 attr = (UMLAttribute *)list->data;
1954 newattr = uml_attribute_copy(attr, newobj);
1956 newumlclass->attributes = g_list_prepend(newumlclass->attributes,
1957 newattr);
1958 list = g_list_next(list);
1961 newumlclass->operations = NULL;
1962 list = umlclass->operations;
1963 while (list != NULL) {
1964 op = (UMLOperation *)list->data;
1965 newop = uml_operation_copy(op);
1966 newop->left_connection->object = newobj;
1967 newop->left_connection->connected = NULL;
1969 newop->right_connection->object = newobj;
1970 newop->right_connection->connected = NULL;
1972 newumlclass->operations = g_list_prepend(newumlclass->operations,
1973 newop);
1974 list = g_list_next(list);
1977 newumlclass->template = umlclass->template;
1979 newumlclass->formal_params = NULL;
1980 list = umlclass->formal_params;
1981 while (list != NULL) {
1982 param = (UMLFormalParameter *)list->data;
1983 newumlclass->formal_params =
1984 g_list_prepend(newumlclass->formal_params,
1985 uml_formalparameter_copy(param));
1986 list = g_list_next(list);
1989 newumlclass->properties_dialog = NULL;
1991 newumlclass->stereotype_string = NULL;
1992 newumlclass->attributes_strings = NULL;
1993 newumlclass->operations_strings = NULL;
1994 newumlclass->operations_wrappos = NULL;
1995 newumlclass->templates_strings = NULL;
1997 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1998 newobj->connections[i] = &newumlclass->connections[i];
1999 newumlclass->connections[i].object = newobj;
2000 newumlclass->connections[i].connected = NULL;
2001 newumlclass->connections[i].pos = umlclass->connections[i].pos;
2002 newumlclass->connections[i].last_pos = umlclass->connections[i].last_pos;
2005 umlclass_calculate_data(newumlclass);
2007 i = UMLCLASS_CONNECTIONPOINTS;
2008 if ( (newumlclass->visible_attributes) &&
2009 (!newumlclass->suppress_attributes)) {
2010 list = newumlclass->attributes;
2011 while (list != NULL) {
2012 attr = (UMLAttribute *)list->data;
2013 printf("Setting copy conn %d of %p to left %p\n", i, newobj, attr->left_connection);
2014 newobj->connections[i++] = attr->left_connection;
2015 printf("Setting copy conn %d of %p to right %p\n", i, newobj, attr->right_connection);
2016 newobj->connections[i++] = attr->right_connection;
2018 list = g_list_next(list);
2022 if ( (newumlclass->visible_operations) &&
2023 (!newumlclass->suppress_operations)) {
2024 list = newumlclass->operations;
2025 while (list != NULL) {
2026 op = (UMLOperation *)list->data;
2027 newobj->connections[i++] = op->left_connection;
2028 newobj->connections[i++] = op->right_connection;
2030 list = g_list_next(list);
2034 #ifdef UML_MAINPOINT
2035 newobj->connections[i] = &newumlclass->connections[UMLCLASS_CONNECTIONPOINTS];
2036 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = newobj;
2037 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
2038 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos =
2039 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos;
2040 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos =
2041 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos;
2042 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags =
2043 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags;
2044 i++;
2045 #endif
2047 umlclass_update_data(newumlclass);
2049 umlclass_sanity_check(newumlclass, "Copied");
2051 return &newumlclass->element.object;
2055 static void
2056 umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
2057 const char *filename)
2059 UMLAttribute *attr;
2060 UMLOperation *op;
2061 UMLFormalParameter *formal_param;
2062 GList *list;
2063 AttributeNode attr_node;
2065 umlclass_sanity_check(umlclass, "Saving");
2067 element_save(&umlclass->element, obj_node);
2069 /* Class info: */
2070 data_add_string(new_attribute(obj_node, "name"),
2071 umlclass->name);
2072 data_add_string(new_attribute(obj_node, "stereotype"),
2073 umlclass->stereotype);
2074 data_add_string(new_attribute(obj_node, "comment"),
2075 umlclass->comment);
2076 data_add_boolean(new_attribute(obj_node, "abstract"),
2077 umlclass->abstract);
2078 data_add_boolean(new_attribute(obj_node, "suppress_attributes"),
2079 umlclass->suppress_attributes);
2080 data_add_boolean(new_attribute(obj_node, "suppress_operations"),
2081 umlclass->suppress_operations);
2082 data_add_boolean(new_attribute(obj_node, "visible_attributes"),
2083 umlclass->visible_attributes);
2084 data_add_boolean(new_attribute(obj_node, "visible_operations"),
2085 umlclass->visible_operations);
2086 data_add_boolean(new_attribute(obj_node, "visible_comments"),
2087 umlclass->visible_comments);
2088 data_add_boolean(new_attribute(obj_node, "wrap_operations"),
2089 umlclass->wrap_operations);
2090 data_add_int(new_attribute(obj_node, "wrap_after_char"),
2091 umlclass->wrap_after_char);
2092 data_add_int(new_attribute(obj_node, "Comment_line_length"),
2093 umlclass->Comment_line_length);
2094 data_add_color(new_attribute(obj_node, "line_color"),
2095 &umlclass->line_color);
2096 data_add_color(new_attribute(obj_node, "fill_color"),
2097 &umlclass->fill_color);
2098 data_add_color(new_attribute(obj_node, "text_color"),
2099 &umlclass->text_color);
2100 data_add_font (new_attribute (obj_node, "normal_font"),
2101 umlclass->normal_font);
2102 data_add_font (new_attribute (obj_node, "abstract_font"),
2103 umlclass->abstract_font);
2104 data_add_font (new_attribute (obj_node, "polymorphic_font"),
2105 umlclass->polymorphic_font);
2106 data_add_font (new_attribute (obj_node, "classname_font"),
2107 umlclass->classname_font);
2108 data_add_font (new_attribute (obj_node, "abstract_classname_font"),
2109 umlclass->abstract_classname_font);
2110 data_add_font (new_attribute (obj_node, "comment_font"),
2111 umlclass->comment_font);
2112 data_add_real (new_attribute (obj_node, "normal_font_height"),
2113 umlclass->font_height);
2114 data_add_real (new_attribute (obj_node, "polymorphic_font_height"),
2115 umlclass->polymorphic_font_height);
2116 data_add_real (new_attribute (obj_node, "abstract_font_height"),
2117 umlclass->abstract_font_height);
2118 data_add_real (new_attribute (obj_node, "classname_font_height"),
2119 umlclass->classname_font_height);
2120 data_add_real (new_attribute (obj_node, "abstract_classname_font_height"),
2121 umlclass->abstract_classname_font_height);
2122 data_add_real (new_attribute (obj_node, "comment_font_height"),
2123 umlclass->comment_font_height);
2125 /* Attribute info: */
2126 attr_node = new_attribute(obj_node, "attributes");
2127 list = umlclass->attributes;
2128 while (list != NULL) {
2129 attr = (UMLAttribute *) list->data;
2130 printf("Writing attr %p\n", attr);
2131 uml_attribute_write(attr_node, attr);
2132 list = g_list_next(list);
2135 /* Operations info: */
2136 attr_node = new_attribute(obj_node, "operations");
2137 list = umlclass->operations;
2138 while (list != NULL) {
2139 op = (UMLOperation *) list->data;
2140 uml_operation_write(attr_node, op);
2141 list = g_list_next(list);
2144 /* Template info: */
2145 data_add_boolean(new_attribute(obj_node, "template"),
2146 umlclass->template);
2148 attr_node = new_attribute(obj_node, "templates");
2149 list = umlclass->formal_params;
2150 while (list != NULL) {
2151 formal_param = (UMLFormalParameter *) list->data;
2152 uml_formalparameter_write(attr_node, formal_param);
2153 list = g_list_next(list);
2157 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
2158 const char *filename)
2160 UMLClass *umlclass;
2161 Element *elem;
2162 DiaObject *obj;
2163 AttributeNode attr_node;
2164 int i;
2165 GList *list;
2168 umlclass = g_malloc0(sizeof(UMLClass));
2169 elem = &umlclass->element;
2170 obj = &elem->object;
2172 obj->type = &umlclass_type;
2173 obj->ops = &umlclass_ops;
2175 element_load(elem, obj_node);
2177 #ifdef UML_MAINPOINT
2178 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1);
2179 #else
2180 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS);
2181 #endif
2183 umlclass->properties_dialog = NULL;
2185 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2186 obj->connections[i] = &umlclass->connections[i];
2187 umlclass->connections[i].object = obj;
2188 umlclass->connections[i].connected = NULL;
2191 fill_in_fontdata(umlclass);
2193 /* kind of dirty, object_load_props() may leave us in an inconsnected = NULL;
2196 fill_in_fontdata(umlclass);
2198 /* kind of dirty, object_load_props() may leave us in an inconsistent state --hb */
2199 object_load_props(obj,obj_node);
2200 /* a bunch of properties still need their own special handling */
2202 /* Class info: */
2204 /* parameters loaded via StdProp dont belong here anymore. In case of strings they
2205 * will produce leaks. Otherwise the are just wasteing time (at runtime and while
2206 * reading the code). Except maybe for some compatibility stuff.
2207 * Although that *could* probably done via StdProp too. --hb
2210 /* new since 0.94, don't wrap by default to keep old diagrams intact */
2211 umlclass->wrap_operations = FALSE;
2212 attr_node = object_find_attribute(obj_node, "wrap_operations");
2213 if (attr_node != NULL)
2214 umlclass->wrap_operations = data_boolean(attribute_first_data(attr_node));
2216 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
2217 attr_node = object_find_attribute(obj_node, "wrap_after_char");
2218 if (attr_node != NULL)
2219 umlclass->wrap_after_char = data_int(attribute_first_data(attr_node));
2221 umlclass->Comment_line_length = UMLCLASS_COMMENT_LINE_LENGTH;
2222 attr_node = object_find_attribute(obj_node,"Comment_line_length");
2223 if (attr_node != NULL)
2225 umlclass->Comment_line_length = data_int(attribute_first_data(attr_node));
2227 umlclass->line_color = color_black;
2228 /* support the old name ... */
2229 attr_node = object_find_attribute(obj_node, "foreground_color");
2230 if(attr_node != NULL)
2231 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2232 umlclass->text_color = umlclass->line_color;
2233 /* ... but prefer the new one */
2234 attr_node = object_find_attribute(obj_node, "line_color");
2235 if(attr_node != NULL)
2236 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2238 umlclass->fill_color = color_white;
2239 /* support the old name ... */
2240 attr_node = object_find_attribute(obj_node, "background_color");
2241 if(attr_node != NULL)
2242 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2243 /* ... but prefer the new one */
2244 attr_node = object_find_attribute(obj_node, "fill_color");
2245 if(attr_node != NULL)
2246 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2248 /* Attribute info: */
2249 list = umlclass->attributes;
2250 while (list) {
2251 UMLAttribute *attr = list->data;
2252 g_assert(attr);
2254 attr->left_connection->object = obj;
2255 attr->left_connection->connected = NULL;
2256 attr->right_connection->object = obj;
2257 attr->right_connection->connected = NULL;
2258 list = g_list_next(list);
2261 /* Operations info: */
2262 list = umlclass->operations;
2263 while (list) {
2264 UMLOperation *op = (UMLOperation *)list->data;
2265 g_assert(op);
2267 op->left_connection->object = obj;
2268 op->left_connection->connected = NULL;
2270 op->right_connection->object = obj;
2271 op->right_connection->connected = NULL;
2272 list = g_list_next(list);
2275 /* Template info: */
2276 umlclass->template = FALSE;
2277 attr_node = object_find_attribute(obj_node, "template");
2278 if (attr_node != NULL)
2279 umlclass->template = data_boolean(attribute_first_data(attr_node));
2281 fill_in_fontdata(umlclass);
2283 umlclass->stereotype_string = NULL;
2284 umlclass->attributes_strings = NULL;
2285 umlclass->operations_strings = NULL;
2286 umlclass->operations_wrappos = NULL;
2287 umlclass->templates_strings = NULL;
2289 umlclass_calculate_data(umlclass);
2291 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
2292 umlclass_update_data(umlclass);
2294 for (i=0;i<8;i++) {
2295 obj->handles[i]->type = HANDLE_NON_MOVABLE;
2298 umlclass_sanity_check(umlclass, "Loaded class");
2300 return &umlclass->element.object;
2303 /** Returns the number of connection points used by the attributes and
2304 * connections in the current state of the object.
2306 static int
2307 umlclass_num_dynamic_connectionpoints(UMLClass *umlclass) {
2308 int num = 0;
2309 if ( (umlclass->visible_attributes) &&
2310 (!umlclass->suppress_attributes)) {
2311 GList *list = umlclass->attributes;
2312 num += 2 * g_list_length(list);
2315 if ( (umlclass->visible_operations) &&
2316 (!umlclass->suppress_operations)) {
2317 GList *list = umlclass->operations;
2318 num += 2 * g_list_length(list);
2320 return num;
2323 void
2324 umlclass_sanity_check(UMLClass *c, gchar *msg)
2326 #ifdef UML_MAINPOINT
2327 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS + 1;
2328 #else
2329 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS;
2330 #endif
2331 DiaObject *obj = (DiaObject*)c;
2332 GList *attrs, *ops;
2333 int i;
2335 dia_object_sanity_check((DiaObject *)c, msg);
2337 /* Check that num_connections is correct */
2338 dia_assert_true(num_fixed_connections + umlclass_num_dynamic_connectionpoints(c)
2339 == obj->num_connections,
2340 "%s: Class %p has %d connections, but %d fixed and %d dynamic\n",
2341 msg, c, obj->num_connections, num_fixed_connections,
2342 umlclass_num_dynamic_connectionpoints(c));
2344 for (i = 0; i < UMLCLASS_CONNECTIONPOINTS; i++) {
2345 dia_assert_true(&c->connections[i] == obj->connections[i],
2346 "%s: Class %p connection mismatch at %d: %p != %p\n",
2347 msg, c, i, &c->connections[i], obj->connections[i]);
2350 #ifdef UML_MAINPOINT
2351 dia_assert_true(&c->connections[i] ==
2352 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2353 "%s: Class %p mainpoint mismatch: %p != %p (at %d)\n",
2354 msg, c, i, &c->connections[i],
2355 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2356 i + umlclass_num_dynamic_connectionpoints(c));
2357 #endif
2359 /* Check that attributes are set up right. */
2360 i = 0;
2361 for (attrs = c->attributes; attrs != NULL; attrs = g_list_next(attrs)) {
2362 UMLAttribute *attr = (UMLAttribute *)attrs->data;
2363 int conn_offset = UMLCLASS_CONNECTIONPOINTS + 2 * i;
2364 dia_assert_true(attr->name != NULL,
2365 "%s: %p attr %d has null name\n",
2366 msg, c, i);
2367 dia_assert_true(attr->type != NULL,
2368 "%s: %p attr %d has null type\n",
2369 msg, c, i);
2370 dia_assert_true(attr->comment != NULL,
2371 "%s: %p attr %d has null comment\n",
2372 msg, c, i);
2374 dia_assert_true(attr->left_connection != NULL,
2375 "%s: %p attr %d has null left connection\n",
2376 msg, c, i);
2377 dia_assert_true(attr->left_connection == obj->connections[conn_offset],
2378 "%s: %p attr %d left conn %p doesn't match obj conn %d: %p\n",
2379 msg, c, i, attr->left_connection,
2380 conn_offset, obj->connections[conn_offset]);
2381 dia_assert_true(attr->right_connection == obj->connections[conn_offset + 1],
2382 "%s: %p attr %d right conn %p doesn't match obj conn %d: %p\n",
2383 msg, c, i, attr->right_connection,
2384 conn_offset + 1, obj->connections[conn_offset + 1]);
2385 dia_assert_true(attr->right_connection != NULL,
2386 "%s: %p attr %d has null right connection\n",
2387 msg, c, i);
2389 i++;
2391 /* Check that operations are set up right. */