UML comments bug #303744
[dia.git] / objects / UML / class.c
blobd55121a49d7b6aa00bdf8196fff4287a297ce589
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
48 static real umlclass_distance_from(UMLClass *umlclass, Point *point);
49 static void umlclass_select(UMLClass *umlclass, Point *clicked_point,
50 DiaRenderer *interactive_renderer);
51 static ObjectChange* umlclass_move_handle(UMLClass *umlclass, Handle *handle,
52 Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers);
53 static ObjectChange* umlclass_move(UMLClass *umlclass, Point *to);
54 static void umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer);
55 static DiaObject *umlclass_create(Point *startpoint,
56 void *user_data,
57 Handle **handle1,
58 Handle **handle2);
59 static void umlclass_destroy(UMLClass *umlclass);
60 static DiaObject *umlclass_copy(UMLClass *umlclass);
62 static void umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
63 const char *filename);
64 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
65 const char *filename);
67 static DiaMenu * umlclass_object_menu(DiaObject *obj, Point *p);
68 static ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data);
70 static PropDescription *umlclass_describe_props(UMLClass *umlclass);
71 static void umlclass_get_props(UMLClass *umlclass, GPtrArray *props);
72 static void umlclass_set_props(UMLClass *umlclass, GPtrArray *props);
74 static void fill_in_fontdata(UMLClass *umlclass);
75 static int umlclass_num_dynamic_connectionpoints(UMLClass *class);
77 static ObjectTypeOps umlclass_type_ops =
79 (CreateFunc) umlclass_create,
80 (LoadFunc) umlclass_load,
81 (SaveFunc) umlclass_save
84 DiaObjectType umlclass_type =
86 "UML - Class", /* name */
87 0, /* version */
88 (char **) umlclass_xpm, /* pixmap */
90 &umlclass_type_ops /* ops */
93 static ObjectOps umlclass_ops = {
94 (DestroyFunc) umlclass_destroy,
95 (DrawFunc) umlclass_draw,
96 (DistanceFunc) umlclass_distance_from,
97 (SelectFunc) umlclass_select,
98 (CopyFunc) umlclass_copy,
99 (MoveFunc) umlclass_move,
100 (MoveHandleFunc) umlclass_move_handle,
101 (GetPropertiesFunc) umlclass_get_properties,
102 (ApplyPropertiesFunc) umlclass_apply_props_from_dialog,
103 (ObjectMenuFunc) umlclass_object_menu,
104 (DescribePropsFunc) umlclass_describe_props,
105 (GetPropsFunc) umlclass_get_props,
106 (SetPropsFunc) umlclass_set_props
109 extern PropDescDArrayExtra umlattribute_extra;
110 extern PropDescDArrayExtra umloperation_extra;
111 extern PropDescDArrayExtra umlparameter_extra;
112 extern PropDescDArrayExtra umlformalparameter_extra;
114 static PropDescription umlclass_props[] = {
115 ELEMENT_COMMON_PROPERTIES,
116 PROP_STD_TEXT_COLOUR_OPTIONAL,
117 PROP_STD_LINE_COLOUR_OPTIONAL,
118 PROP_STD_FILL_COLOUR_OPTIONAL,
120 PROP_STD_NOTEBOOK_BEGIN,
121 PROP_NOTEBOOK_PAGE("class", PROP_FLAG_DONT_MERGE, N_("Class")),
122 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
123 N_("Name"), NULL, NULL },
124 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
125 N_("Stereotype"), NULL, NULL },
126 { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
127 N_("Comment"), NULL, NULL },
128 { "abstract", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
129 N_("Abstract"), NULL, NULL },
130 { "template", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL| PROP_FLAG_NO_DEFAULTS,
131 N_("Template"), NULL, NULL },
133 { "suppress_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
134 N_("Suppress Attributes"), NULL, NULL },
135 { "suppress_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
136 N_("Suppress Operations"), NULL, NULL },
137 { "visible_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
138 N_("Visible Attributes"), NULL, NULL },
139 { "visible_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
140 N_("Visible Operations"), NULL, NULL },
141 { "visible_comments", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
142 N_("Visible Comments"), NULL, NULL },
143 { "wrap_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
144 N_("Wrap Operations"), NULL, NULL },
145 { "wrap_after_char", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
146 N_("Wrap after char"), NULL, NULL },
147 { "Comment_line_length", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
148 N_("Comment line length"), NULL, NULL},
150 /* all this just to make the defaults selectable ... */
151 PROP_NOTEBOOK_PAGE("font", PROP_FLAG_DONT_MERGE, N_("Font")),
152 PROP_STD_MULTICOL_BEGIN,
153 PROP_MULTICOL_COLUMN("font"),
154 /* FIXME: apparently multicol does not work correctly, this should be FIRST column */
155 { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
156 N_("Normal"), NULL, NULL },
157 { "polymorphic_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
158 N_("Polymorphic"), NULL, NULL },
159 { "abstract_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
160 N_("Abstract"), NULL, NULL },
161 { "classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
162 N_("Classname"), NULL, NULL },
163 { "abstract_classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
164 N_("Abstract Classname"), NULL, NULL },
165 { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
166 N_("Comment"), NULL, NULL },
168 PROP_MULTICOL_COLUMN("height"),
169 { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
170 N_(" "), NULL, NULL },
171 { "polymorphic_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
172 N_(" "), NULL, NULL },
173 { "abstract_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
174 N_(" "), NULL, NULL },
175 { "classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
176 N_(" "), NULL, NULL },
177 { "abstract_classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
178 N_(" "), NULL, NULL },
179 { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
180 N_(" "), NULL, NULL },
181 PROP_STD_MULTICOL_END,
182 PROP_STD_NOTEBOOK_END,
184 /* these are used during load, but currently not during save */
185 { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
186 N_("Attributes"), NULL, NULL /* umlattribute_extra */ },
187 { "operations", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
188 N_("Operations"), NULL, NULL /* umloperations_extra */ },
189 /* the naming is questionable, but kept for compatibility */
190 { "templates", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
191 N_("Template Parameters"), NULL, NULL /* umlformalparameters_extra */ },
193 PROP_DESC_END
196 static PropDescription *
197 umlclass_describe_props(UMLClass *umlclass)
199 if (umlclass_props[0].quark == 0) {
200 int i = 0;
202 prop_desc_list_calculate_quarks(umlclass_props);
203 while (umlclass_props[i].name != NULL) {
204 /* can't do this static, at least not on win32
205 * due to relocation (initializer not a constant)
207 if (0 == strcmp(umlclass_props[i].name, "attributes"))
208 umlclass_props[i].extra_data = &umlattribute_extra;
209 else if (0 == strcmp(umlclass_props[i].name, "operations")) {
210 PropDescription *records = umloperation_extra.common.record;
211 int j = 0;
213 umlclass_props[i].extra_data = &umloperation_extra;
214 while (records[j].name != NULL) {
215 if (0 == strcmp(records[j].name, "parameters"))
216 records[j].extra_data = &umlparameter_extra;
217 j++;
220 else if (0 == strcmp(umlclass_props[i].name, "templates"))
221 umlclass_props[i].extra_data = &umlformalparameter_extra;
223 i++;
226 return umlclass_props;
229 static PropOffset umlclass_offsets[] = {
230 ELEMENT_COMMON_PROPERTIES_OFFSETS,
232 { "text_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, text_color) },
233 { "line_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, line_color) },
234 { "fill_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, fill_color) },
235 { "name", PROP_TYPE_STRING, offsetof(UMLClass, name) },
236 { "stereotype", PROP_TYPE_STRING, offsetof(UMLClass, stereotype) },
237 { "comment", PROP_TYPE_STRING, offsetof(UMLClass, comment) },
238 { "abstract", PROP_TYPE_BOOL, offsetof(UMLClass, abstract) },
239 { "template", PROP_TYPE_BOOL, offsetof(UMLClass, template) },
240 { "suppress_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_attributes) },
241 { "visible_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , visible_attributes) },
242 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
243 { "suppress_operations", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_operations) },
244 { "visible_operations", PROP_TYPE_BOOL, offsetof(UMLClass , visible_operations) },
245 { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
246 { "wrap_operations", PROP_TYPE_BOOL, offsetof(UMLClass , wrap_operations) },
247 { "wrap_after_char", PROP_TYPE_INT, offsetof(UMLClass , wrap_after_char) },
248 { "Comment_line_length", PROP_TYPE_INT, offsetof(UMLClass, Comment_line_length) },
250 /* all this just to make the defaults selectable ... */
251 PROP_OFFSET_STD_MULTICOL_BEGIN,
252 PROP_OFFSET_MULTICOL_COLUMN("font"),
253 { "normal_font", PROP_TYPE_FONT, offsetof(UMLClass, normal_font) },
254 { "abstract_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_font) },
255 { "polymorphic_font", PROP_TYPE_FONT, offsetof(UMLClass, polymorphic_font) },
256 { "classname_font", PROP_TYPE_FONT, offsetof(UMLClass, classname_font) },
257 { "abstract_classname_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_classname_font) },
258 { "comment_font", PROP_TYPE_FONT, offsetof(UMLClass, comment_font) },
260 PROP_OFFSET_MULTICOL_COLUMN("height"),
261 { "normal_font_height", PROP_TYPE_REAL, offsetof(UMLClass, font_height) },
262 { "abstract_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_font_height) },
263 { "polymorphic_font_height", PROP_TYPE_REAL, offsetof(UMLClass, polymorphic_font_height) },
264 { "classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, classname_font_height) },
265 { "abstract_classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_classname_font_height) },
266 { "comment_font_height", PROP_TYPE_REAL, offsetof(UMLClass, comment_font_height) },
267 PROP_OFFSET_STD_MULTICOL_END,
269 { "operations", PROP_TYPE_DARRAY, offsetof(UMLClass , operations) },
270 { "attributes", PROP_TYPE_DARRAY, offsetof(UMLClass , attributes) } ,
271 { "templates", PROP_TYPE_DARRAY, offsetof(UMLClass , formal_params) } ,
273 { NULL, 0, 0 },
276 static void
277 umlclass_get_props(UMLClass * umlclass, GPtrArray *props)
279 object_get_props_from_offsets(&umlclass->element.object,
280 umlclass_offsets, props);
283 static DiaMenuItem umlclass_menu_items[] = {
284 { N_("Show Comments"), umlclass_show_comments_callback, NULL,
285 DIAMENU_ACTIVE|DIAMENU_TOGGLE },
288 static DiaMenu umlclass_menu = {
289 N_("Class"),
290 sizeof(umlclass_menu_items)/sizeof(DiaMenuItem),
291 umlclass_menu_items,
292 NULL
295 DiaMenu *
296 umlclass_object_menu(DiaObject *obj, Point *p)
298 umlclass_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
299 (((UMLClass *)obj)->visible_comments?DIAMENU_TOGGLE_ON:0);
301 return &umlclass_menu;
304 ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data)
306 ObjectChange *change = new_object_state_change(obj, NULL, NULL, NULL );
308 ((UMLClass *)obj)->visible_comments = !((UMLClass *)obj)->visible_comments;
309 umlclass_calculate_data((UMLClass *)obj);
310 umlclass_update_data((UMLClass *)obj);
311 return change;
314 static void
315 umlclass_set_props(UMLClass *umlclass, GPtrArray *props)
317 /* now that operations/attributes can be set here as well we need to
318 * take for the number of connections update as well
319 * Note that due to a hack in umlclass_load, this is called before
320 * the normal connection points are set up.
322 DiaObject *obj = &umlclass->element.object;
323 GList *list;
324 int num;
326 object_set_props_from_offsets(&umlclass->element.object, umlclass_offsets,
327 props);
329 num = UMLCLASS_CONNECTIONPOINTS + umlclass_num_dynamic_connectionpoints(umlclass);
331 #ifdef UML_MAINPOINT
332 obj->num_connections = num + 1;
333 #else
334 obj->num_connections = num;
335 #endif
337 obj->connections = g_realloc(obj->connections, obj->num_connections*sizeof(ConnectionPoint *));
339 /* Update data: */
340 if (num > UMLCLASS_CONNECTIONPOINTS) {
341 int i;
342 /* this is just updating pointers to ConnectionPoint, the real connection handling is elsewhere.
343 * Note: Can't optimize here on number change cause the ops/attribs may have changed regardless of that.
345 i = UMLCLASS_CONNECTIONPOINTS;
346 list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
347 while (list != NULL) {
348 UMLAttribute *attr = (UMLAttribute *)list->data;
350 printf("Setting obj conn %d to %p->left: %p\n", i, attr, attr->left_connection);
351 obj->connections[i] = attr->left_connection;
352 obj->connections[i]->object = obj;
353 i++;
354 printf("Setting obj conn %d to %p->right: %p\n", i, attr, attr->right_connection);
355 obj->connections[i] = attr->right_connection;
356 obj->connections[i]->object = obj;
357 i++;
358 list = g_list_next(list);
360 list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
361 while (list != NULL) {
362 UMLOperation *op = (UMLOperation *)list->data;
363 obj->connections[i] = op->left_connection;
364 obj->connections[i]->object = obj;
365 i++;
366 obj->connections[i] = op->right_connection;
367 obj->connections[i]->object = obj;
368 i++;
369 list = g_list_next(list);
372 #ifdef UML_MAINPOINT
373 obj->connections[num] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
374 obj->connections[num]->object = obj;
375 #endif
377 umlclass_calculate_data(umlclass);
378 umlclass_update_data(umlclass);
379 /* Would like to sanity check here, but the call to object_load_props
380 * in umlclass_load means we will be called with inconsistent data. */
381 umlclass_sanity_check(umlclass, "After updating data");
384 static real
385 umlclass_distance_from(UMLClass *umlclass, Point *point)
387 DiaObject *obj = &umlclass->element.object;
388 return distance_rectangle_point(&obj->bounding_box, point);
391 static void
392 umlclass_select(UMLClass *umlclass, Point *clicked_point,
393 DiaRenderer *interactive_renderer)
395 element_update_handles(&umlclass->element);
398 static ObjectChange*
399 umlclass_move_handle(UMLClass *umlclass, Handle *handle,
400 Point *to, ConnectionPoint *cp,
401 HandleMoveReason reason, ModifierKeys modifiers)
403 assert(umlclass!=NULL);
404 assert(handle!=NULL);
405 assert(to!=NULL);
407 assert(handle->id < UMLCLASS_CONNECTIONPOINTS);
409 return NULL;
412 static ObjectChange*
413 umlclass_move(UMLClass *umlclass, Point *to)
415 umlclass->element.corner = *to;
416 umlclass_update_data(umlclass);
418 return NULL;
423 static void
424 uml_underline_text(DiaRenderer *renderer,
425 Point StartPoint,
426 DiaFont *font,
427 real font_height,
428 gchar *attstr,
429 Color *color,
430 real line_width,
431 real underline_width)
433 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
434 Point UnderlineStartPoint;
435 Point UnderlineEndPoint;
437 UnderlineStartPoint = StartPoint;
438 UnderlineStartPoint.y += font_height * 0.1;
439 UnderlineEndPoint = UnderlineStartPoint;
440 UnderlineEndPoint.x += dia_font_string_width(attstr, font, font_height);
441 renderer_ops->set_linewidth(renderer, underline_width);
442 renderer_ops->draw_line(renderer, &UnderlineStartPoint, &UnderlineEndPoint, color);
443 renderer_ops->set_linewidth(renderer, line_width);
447 ** uml_create_documentation_tag
449 * FILENAME: \dia\objects\UML\class.c
451 * PARAMETERS:
452 * comment - The comment to be wrapped to the line length limit
453 * WrapPoint - The maximum line length allowed for the line.
454 * NumberOfLines - The number of comment lines after the wrapping.
456 * DESCRIPTION:
457 * This function takes a string of characters and creates a
458 * documentation tagged string which is also a wrapped string
459 * where no line is longer than the value of WrapPoint.
461 * First a string is created containing only the text
462 * "{documentation = ". Then the contents of the comment string
463 * are added but wrapped. This is done by first looking for any
464 * New Line characters. If the line segment is longer than the
465 * WrapPoint would allow, the line is broken at either the
466 * first whitespace before the WrapPoint or if there are no
467 * whitespaces in the segment, at the WrapPoint. This
468 * continues until the entire string has been processed and
469 * then the resulting new string is returned. No attempt is
470 * made to rejoin any of the segments, that is all New Lines
471 * are treated as hard newlines. No syllable matching is done
472 * either so breaks in words will sometimes not make real
473 * sense.
475 * Finally, since this function returns newly created dynamic
476 * memory the caller must free the memory to prevent memory
477 * leaks.
479 * RETURNS:
480 * A pointer to the string containing the line breakpoints for
481 * wrapping.
483 * NOTE:
484 * This function should most likely be move to a source file for
485 * handling global UML functionallity at some point.
487 static gchar *
488 uml_create_documentation_tag(gchar * comment,gint WrapPoint, gint *NumberOfLines)
490 gchar *CommentTag = "{documentation = ";
491 gint TagLength = strlen(CommentTag);
492 gchar *WrappedComment = g_malloc(TagLength+1);
493 gint LengthOfComment = strlen(comment);
494 gint CommentIndex = 0;
495 gint LengthOfWrappedComment= 0;
496 gint LineLen = WrapPoint - TagLength;
498 WrappedComment[0] = '\0';
499 strcat(WrappedComment, CommentTag);
500 LengthOfWrappedComment = strlen(WrappedComment);
501 *NumberOfLines = 1;
503 /* Remove leading whitespace */
504 while( isspace(comment[CommentIndex])){
505 CommentIndex++;
509 while( CommentIndex < LengthOfComment) /* more of the comment to go? */
511 gchar *Nl = strchr(&comment[CommentIndex], '\n');
512 gint BytesToNextNewLine = 0;
514 /* if this is the first line then we have to take into
515 * account the tag of the tagged value
518 LengthOfWrappedComment = strlen(WrappedComment);
520 * First handle the next new lines
522 if ( Nl != NULL){
523 BytesToNextNewLine = (Nl - &comment[CommentIndex]);
526 if ((Nl != NULL) && (BytesToNextNewLine < LineLen)){
527 LineLen = BytesToNextNewLine;
529 else{
530 if( (CommentIndex + LineLen) > LengthOfComment){
531 LineLen = LengthOfComment-CommentIndex;
533 while (LineLen > 0){
534 if ((LineLen == strlen(&comment[CommentIndex])) ||
535 isspace(comment[CommentIndex+LineLen])){
536 break;
537 } else{
538 LineLen--;
541 if ((*NumberOfLines > 1) &&( LineLen == 0)){
542 LineLen = WrapPoint;
545 if (LineLen < 0){
546 LineLen = 0;
549 /* Grow the wrapped text to make room for the NL and the next chunk */
550 WrappedComment = g_realloc(WrappedComment,LengthOfWrappedComment+LineLen+2);
551 memset(&WrappedComment[LengthOfWrappedComment],0,LineLen+2);
552 strncat(WrappedComment, &comment[CommentIndex], LineLen);
554 CommentIndex += LineLen;
555 while( isspace(comment[CommentIndex])){
556 CommentIndex++;
558 if (CommentIndex < LengthOfComment){
559 /* if this is not the last line add a new-line*/
560 strcat(WrappedComment,"\n");
561 *NumberOfLines+=1;
563 LengthOfWrappedComment = strlen(WrappedComment);
564 LineLen = WrapPoint;
566 WrappedComment = g_realloc(WrappedComment,LengthOfWrappedComment+2);
567 strcat(WrappedComment, "}");
568 return WrappedComment;
572 ** uml_draw_comments
574 * FILENAME: \dia\objects\UML\class.c
576 * PARAMETERS:
577 * renderer - The Renderer on which the comment is being drawn.
578 * *font - The font to render the comment in.
579 * font_height - The Y size of the font used to render the comment
580 * *text_color - A pointer to the color to use to render the comment
581 * *comment - The comment string to render
582 * Comment_line_length-The maximum length of any one line in the comment
583 * *p - The point at which the comment is to start.
584 * alignment - The method to use for alignment of the font.
586 * DESCRIPTION:
587 * Draw the comment at the point, p, using the comment font from the
588 * class defined by umlclass. When complete update the point to reflect
589 * the size of data drawn.
590 * The comment will have been word wrapped using the function
591 * uml_create_documentation_tag, so it may have more than one line on the
592 * display.
594 * RETURNS: void, No useful information is returned.
598 static void
599 uml_draw_comments(DiaRenderer *renderer,
600 DiaFont *font,
601 real font_height,
602 Color *text_color,
603 gchar *comment,
604 gint Comment_line_length,
605 Point *p,
606 gint alignment)
608 gint NumberOfLines = 0;
609 gint Index;
610 gchar *CommentString = 0;
611 gchar *NewLineP= NULL;
612 gchar *RenderP;
614 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
616 CommentString =
617 uml_create_documentation_tag(comment, Comment_line_length, &NumberOfLines);
618 RenderP = CommentString;
619 renderer_ops->set_font(renderer, font, font_height);
620 for ( Index=0; Index < NumberOfLines; Index++)
622 p->y += font_height; /* Advance to the next line */
623 NewLineP = strchr(RenderP, '\n');
624 if ( NewLineP != NULL)
626 *NewLineP++ = '\0';
628 renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
629 RenderP = NewLineP;
630 if ( NewLineP == NULL){
631 break;
634 g_free(CommentString);
639 ** umlclass_draw_namebox
641 * FILENAME: \dia\objects\UML\class.c
643 * PARAMETERS:
644 * umlclass - The pointer to the class being drawn
645 * renderer - The pointer to the rendering object used to draw
646 * elem - The pointer to the element within the class to be drawn
648 * DESCRIPTION:
650 * RETURNS:
651 * The offset from the start of the class to the bottom of the namebox
654 static real
655 umlclass_draw_namebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem )
657 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
658 real font_height;
659 DiaFont *font;
660 Point StartPoint;
661 Point LowerRightPoint;
662 real Yoffset;
663 Color *text_color = &umlclass->text_color;
665 StartPoint.x = elem->corner.x;
666 StartPoint.y = elem->corner.y;
667 Yoffset = elem->corner.y + umlclass->namebox_height;
669 LowerRightPoint = StartPoint;
670 LowerRightPoint.x += elem->width;
671 LowerRightPoint.y = Yoffset;
674 * First draw the outer box and fill color for the class name
675 * object
677 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->fill_color);
678 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->line_color);
680 /* Start at the midpoint on the X axis */
681 StartPoint.x += elem->width / 2.0;
683 /* stereotype: */
684 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
685 gchar *String = umlclass->stereotype_string;
686 StartPoint.y += 0.1;
687 StartPoint.y += dia_font_ascent(String, umlclass->normal_font, umlclass->font_height);
688 renderer_ops->set_font(renderer, umlclass->normal_font, umlclass->font_height);
689 renderer_ops->draw_string(renderer, String, &StartPoint, ALIGN_CENTER, text_color);
692 /* name: */
693 if (umlclass->name != NULL) {
694 if (umlclass->abstract) {
695 font = umlclass->abstract_classname_font;
696 font_height = umlclass->abstract_classname_font_height;
697 } else {
698 font = umlclass->classname_font;
699 font_height = umlclass->classname_font_height;
701 StartPoint.y += font_height;
703 renderer_ops->set_font(renderer, font, font_height);
704 renderer_ops->draw_string(renderer, umlclass->name, &StartPoint, ALIGN_CENTER, text_color);
707 /* comment */
708 if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0'){
709 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
710 &umlclass->text_color, umlclass->comment,
711 umlclass->Comment_line_length, &StartPoint, ALIGN_CENTER);
713 return Yoffset;
717 ** umlclass_draw_attributebox
719 * FILENAME: \dia\objects\UML\class.c
721 * PARAMETERS:
722 * umlclass - The pointer to the class being drawn
723 * renderer - The pointer to the rendering object used to draw
724 * elem - The pointer to the element within the class to be drawn
725 * Yoffset - The Y offset from the start of the class at which to draw
726 * the attributebox
728 * DESCRIPTION:
730 * RETURNS:
731 * The offset from the start of the class to the bottom of the attributebox
733 static real
734 umlclass_draw_attributebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
736 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
737 real font_height;
738 Point StartPoint;
739 Point LowerRight;
740 DiaFont *font;
741 Color *fill_color = &umlclass->fill_color;
742 Color *line_color = &umlclass->line_color;
743 Color *text_color = &umlclass->text_color;
744 GList *list;
746 StartPoint.x = elem->corner.x;
747 StartPoint.y = Yoffset;
748 Yoffset += umlclass->attributesbox_height;
750 LowerRight = StartPoint;
751 LowerRight.x += elem->width;
752 LowerRight.y = Yoffset;
754 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
755 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
757 if (!umlclass->suppress_attributes) {
758 gint i = 0;
759 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
760 StartPoint.y += 0.1;
762 list = umlclass->attributes;
763 while (list != NULL)
765 UMLAttribute *attr = (UMLAttribute *)list->data;
766 gchar *attstr = g_list_nth(umlclass->attributes_strings, i)->data;
768 if (attr->abstract) {
769 font = umlclass->abstract_font;
770 font_height = umlclass->abstract_font_height;
772 else {
773 font = umlclass->normal_font;
774 font_height = umlclass->font_height;
776 StartPoint.y += font_height;
777 renderer_ops->set_font (renderer, font, font_height);
778 renderer_ops->draw_string(renderer, attstr, &StartPoint, ALIGN_LEFT, text_color);
780 if (attr->class_scope) {
781 uml_underline_text(renderer, StartPoint, font, font_height, attstr, line_color,
782 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
785 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
786 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
787 &umlclass->text_color, attr->comment,
788 umlclass->Comment_line_length, &StartPoint, ALIGN_LEFT);
789 StartPoint.y += umlclass->comment_font_height/2;
791 list = g_list_next(list);
792 i++;
795 return Yoffset;
800 ** umlclass_draw_operationbox
802 * FILENAME: \dia\objects\UML\class.c
804 * PARAMETERS:
805 * umlclass - The pointer to the class being drawn
806 * renderer - The pointer to the rendering object used to draw
807 * elem - The pointer to the element within the class to be drawn
808 * Yoffset - The Y offset from the start of the class at which to draw
809 * the operationbox
811 * DESCRIPTION:
813 * RETURNS:
814 * The offset from the start of the class to the bottom of the operationbox
817 static real
818 umlclass_draw_operationbox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
820 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
821 real font_height;
822 Point StartPoint;
823 Point LowerRight;
824 DiaFont *font;
825 GList *list;
826 Color *fill_color = &umlclass->fill_color;
827 Color *line_color = &umlclass->line_color;
828 Color *text_color = &umlclass->text_color;
831 StartPoint.x = elem->corner.x;
832 StartPoint.y = Yoffset;
833 Yoffset += umlclass->operationsbox_height;
835 LowerRight = StartPoint;
836 LowerRight.x += elem->width;
837 LowerRight.y = Yoffset;
839 renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
840 renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
842 if (!umlclass->suppress_operations) {
843 gint i = 0;
844 GList *wrapsublist = NULL;
845 gchar *part_opstr = NULL;
846 int wrap_pos, last_wrap_pos, ident, wrapping_needed;
847 int part_opstr_len = 0, part_opstr_need = 0;
849 StartPoint.x += (UMLCLASS_BORDER/2.0 + 0.1);
850 StartPoint.y += 0.1;
852 list = umlclass->operations;
853 while (list != NULL) {
854 UMLOperation *op = (UMLOperation *)list->data;
855 gchar* opstr;
856 real ascent;
858 switch (op->inheritance_type) {
859 case UML_ABSTRACT:
860 font = umlclass->abstract_font;
861 font_height = umlclass->abstract_font_height;
862 break;
863 case UML_POLYMORPHIC:
864 font = umlclass->polymorphic_font;
865 font_height = umlclass->polymorphic_font_height;
866 break;
867 case UML_LEAF:
868 default:
869 font = umlclass->normal_font;
870 font_height = umlclass->font_height;
873 wrapping_needed = 0;
874 opstr = (gchar*) g_list_nth(umlclass->operations_strings, i)->data;
875 if( umlclass->wrap_operations ) {
876 wrapsublist = (GList*)g_list_nth( umlclass->operations_wrappos, i)->data;
877 wrapping_needed = GPOINTER_TO_INT( wrapsublist->data );
880 ascent = dia_font_ascent(opstr, font, font_height);
881 renderer_ops->set_font(renderer, font, font_height);
883 if( umlclass->wrap_operations && wrapping_needed) {
885 wrapsublist = g_list_next( wrapsublist);
886 ident = GPOINTER_TO_INT( wrapsublist->data);
887 wrapsublist = g_list_next( wrapsublist);
888 wrap_pos = last_wrap_pos = 0;
890 while( wrapsublist != NULL) {
891 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
893 if( last_wrap_pos == 0) {
894 part_opstr_need = wrap_pos + 1;
895 if (part_opstr_len < part_opstr_need) {
896 part_opstr_len = part_opstr_need;
897 part_opstr = g_realloc (part_opstr, part_opstr_need);
899 strncpy( part_opstr, opstr, wrap_pos);
900 memset( part_opstr+wrap_pos, '\0', 1);
902 else {
903 part_opstr_need = ident + wrap_pos - last_wrap_pos + 1;
904 if (part_opstr_len < part_opstr_need) {
905 part_opstr_len = part_opstr_need;
906 part_opstr = g_realloc (part_opstr, part_opstr_need);
908 memset( part_opstr, ' ', ident);
909 memset( part_opstr+ident, '\0', 1);
910 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
913 StartPoint.y += ascent;
914 renderer_ops->draw_string(renderer, part_opstr, &StartPoint, ALIGN_LEFT, text_color);
915 last_wrap_pos = wrap_pos;
916 wrapsublist = g_list_next( wrapsublist);
919 else
921 StartPoint.y += ascent;
922 renderer_ops->draw_string(renderer, opstr, &StartPoint, ALIGN_LEFT, text_color);
925 if (op->class_scope) {
926 uml_underline_text(renderer, StartPoint, font, font_height, opstr, line_color,
927 UMLCLASS_BORDER, UMLCLASS_UNDERLINEWIDTH );
930 StartPoint.y += font_height - ascent;
932 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
933 uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
934 &umlclass->text_color, op->comment,
935 umlclass->Comment_line_length, &StartPoint, ALIGN_LEFT);
938 list = g_list_next(list);
939 i++;
941 if (part_opstr){
942 g_free(part_opstr);
945 return Yoffset;
949 ** umlclass_draw_template_parameters_box
951 * FILENAME: \dia\objects\UML\class.c
953 * PARAMETERS:
954 * umlclass - The pointer to the class being drawn
955 * renderer - The pointer to the rendering object used to draw
956 * elem - The pointer to the element within the class to be drawn
958 * DESCRIPTION:
960 * This function draws the template parameters box in the upper
961 * right hand corner of the class box for paramertize classes
962 * (aka template classes). It then fills in this box with the
963 * parameters for the class.
965 * At this time there is no provision for adding comments or
966 * documentation to the display.
968 * RETURNS:
969 * void.
972 static void
973 umlclass_draw_template_parameters_box(UMLClass *umlclass, DiaRenderer *renderer, Element *elem)
975 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
976 Point UpperLeft;
977 Point LowerRight;
978 Point TextInsert;
979 GList *list;
980 gint i;
981 DiaFont *font = umlclass->normal_font;
982 real font_height = umlclass->font_height;
983 Color *fill_color = &umlclass->fill_color;
984 Color *line_color = &umlclass->line_color;
985 Color *text_color = &umlclass->text_color;
987 UpperLeft.x = elem->corner.x + elem->width - 2.3;
988 UpperLeft.y = elem->corner.y - umlclass->templates_height + 0.3;
989 TextInsert = UpperLeft;
990 LowerRight = UpperLeft;
991 LowerRight.x += umlclass->templates_width;
992 LowerRight.y += umlclass->templates_height;
994 renderer_ops->fill_rect(renderer, &UpperLeft, &LowerRight, fill_color);
995 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
996 renderer_ops->set_dashlength(renderer, 0.3);
997 renderer_ops->draw_rect(renderer, &UpperLeft, &LowerRight, line_color);
999 TextInsert.x += 0.3;
1000 renderer_ops->set_font(renderer, font, font_height);
1001 i = 0;
1002 list = umlclass->formal_params;
1003 while (list != NULL)
1005 gchar *ParameterString = umlclass->templates_strings[i];
1007 TextInsert.y +=(0.1 + dia_font_ascent(ParameterString, font, font_height));
1008 renderer_ops->draw_string(renderer, ParameterString, &TextInsert, ALIGN_LEFT, text_color);
1010 list = g_list_next(list);
1011 i++;
1016 ** umlclass_draw
1018 * FILENAME: \dia\objects\UML\class.c
1020 * PARAMETERS:
1022 * DESCRIPTION:
1024 * Important Note from earlier contributer:
1025 * Most of this crap could be rendered much more efficiently
1026 * (and probably much cleaner as well) using marked-up
1027 * Pango layout text.
1028 * RETURNS:
1031 static void
1032 umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer)
1034 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1035 real y = 0.0;
1036 Element *elem;
1038 assert(umlclass != NULL);
1039 assert(renderer != NULL);
1041 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
1042 renderer_ops->set_linewidth(renderer, UMLCLASS_BORDER);
1043 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
1045 elem = &umlclass->element;
1047 y = umlclass_draw_namebox(umlclass, renderer, elem);
1048 if (umlclass->visible_attributes) {
1049 y = umlclass_draw_attributebox(umlclass, renderer, elem, y);
1051 if (umlclass->visible_operations) {
1052 y = umlclass_draw_operationbox(umlclass, renderer, elem, y);
1054 if (umlclass->template) {
1055 umlclass_draw_template_parameters_box(umlclass, renderer, elem);
1059 void
1060 umlclass_update_data(UMLClass *umlclass)
1062 Element *elem = &umlclass->element;
1063 DiaObject *obj = &elem->object;
1064 real x,y;
1065 GList *list;
1066 int i;
1067 int pointswide;
1068 int lowerleftcorner;
1069 real pointspacing;
1071 x = elem->corner.x;
1072 y = elem->corner.y;
1074 /* Update connections: */
1075 umlclass->connections[0].pos = elem->corner;
1076 umlclass->connections[0].directions = DIR_NORTH|DIR_WEST;
1078 /* there are four corner points and two side points, thus all
1079 * remaining points are on the top/bottom width
1081 pointswide = (UMLCLASS_CONNECTIONPOINTS - 6) / 2;
1082 pointspacing = elem->width / (pointswide + 1.0);
1084 /* across the top connection points */
1085 for (i=1;i<=pointswide;i++) {
1086 umlclass->connections[i].pos.x = x + (pointspacing * i);
1087 umlclass->connections[i].pos.y = y;
1088 umlclass->connections[i].directions = DIR_NORTH;
1091 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 2;
1092 umlclass->connections[i].pos.x = x + elem->width;
1093 umlclass->connections[i].pos.y = y;
1094 umlclass->connections[i].directions = DIR_NORTH|DIR_EAST;
1096 i = (UMLCLASS_CONNECTIONPOINTS / 2) - 1;
1097 umlclass->connections[i].pos.x = x;
1098 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1099 umlclass->connections[i].directions = DIR_WEST;
1101 i = (UMLCLASS_CONNECTIONPOINTS / 2);
1102 umlclass->connections[i].pos.x = x + elem->width;
1103 umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1104 umlclass->connections[i].directions = DIR_EAST;
1106 i = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1107 umlclass->connections[i].pos.x = x;
1108 umlclass->connections[i].pos.y = y + elem->height;
1109 umlclass->connections[i].directions = DIR_WEST|DIR_SOUTH;
1111 /* across the bottom connection points */
1112 lowerleftcorner = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1113 for (i=1;i<=pointswide;i++) {
1114 umlclass->connections[lowerleftcorner + i].pos.x = x + (pointspacing * i);
1115 umlclass->connections[lowerleftcorner + i].pos.y = y + elem->height;
1116 umlclass->connections[lowerleftcorner + i].directions = DIR_SOUTH;
1119 /* bottom-right corner */
1120 i = (UMLCLASS_CONNECTIONPOINTS) - 1;
1121 umlclass->connections[i].pos.x = x + elem->width;
1122 umlclass->connections[i].pos.y = y + elem->height;
1123 umlclass->connections[i].directions = DIR_EAST|DIR_SOUTH;
1125 #ifdef UML_MAINPOINT
1126 /* Main point -- lives just after fixed connpoints in umlclass array */
1127 i = UMLCLASS_CONNECTIONPOINTS;
1128 umlclass->connections[i].pos.x = x + elem->width / 2;
1129 umlclass->connections[i].pos.y = y + elem->height / 2;
1130 umlclass->connections[i].directions = DIR_ALL;
1131 umlclass->connections[i].flags = CP_FLAGS_MAIN;
1132 #endif
1134 y += umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1136 list = umlclass->attributes;
1137 while (list != NULL) {
1138 UMLAttribute *attr = (UMLAttribute *)list->data;
1140 attr->left_connection->pos.x = x;
1141 attr->left_connection->pos.y = y;
1142 attr->left_connection->directions = DIR_WEST;
1143 attr->right_connection->pos.x = x + elem->width;
1144 attr->right_connection->pos.y = y;
1145 attr->right_connection->directions = DIR_EAST;
1147 y += umlclass->font_height;
1148 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1149 y += umlclass->comment_font_height;
1151 list = g_list_next(list);
1154 y = elem->corner.y + umlclass->namebox_height +
1155 umlclass->attributesbox_height + 0.1 + umlclass->font_height/2;
1157 list = umlclass->operations;
1158 while (list != NULL) {
1159 UMLOperation *op = (UMLOperation *)list->data;
1161 op->left_connection->pos.x = x;
1162 op->left_connection->pos.y = y;
1163 op->left_connection->directions = DIR_WEST;
1164 op->right_connection->pos.x = x + elem->width;
1165 op->right_connection->pos.y = y;
1166 op->right_connection->directions = DIR_EAST;
1168 y += umlclass->font_height;
1169 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0')
1170 y += umlclass->comment_font_height;
1172 list = g_list_next(list);
1175 element_update_boundingbox(elem);
1178 obj->position = elem->corner;
1180 element_update_handles(elem);
1182 umlclass_sanity_check(umlclass, "After updating data");
1188 * umlclass_calculate_name_data
1190 * FILENAME: \dia\objects\UML\class.c
1192 * PARAMETERS:
1193 * umlclass - the class being rendered
1195 * DESCRIPTION:
1196 * This function calculates the height of the class bounding box for
1197 * the name and returns the width of that box. The height is stored
1198 * in the class structure.
1199 * When calculating the comment, if any, the comment is word wrapped
1200 * and the resulting number of lines is then used to calculate the
1201 * height of the bounding box.
1203 * RETURNS:
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;
1265 ** umlclass_calculate_attribute_data
1267 * FILENAME: \dia\objects\UML\class.c
1269 * PARAMETERS:
1270 * umlclass - The class to be drawn.
1272 * DESCRIPTION:
1273 * Calculate the bounding box for the attributes. Include the
1274 * comments if enabled and present.
1276 * RETURNS:
1277 * The real width of the attribute bounding box.
1281 static real
1282 umlclass_calculate_attribute_data(UMLClass *umlclass)
1284 int i;
1285 int pos_next_comma;
1286 int pos_brace;
1287 int wrap_pos;
1288 int last_wrap_pos;
1289 int maxlinewidth;
1290 int length;
1291 real maxwidth = 0.0;
1292 real width = 0.0;
1293 GList *list;
1295 /* attributes box: */
1296 if (umlclass->attributes_strings != NULL)
1298 g_list_foreach(umlclass->attributes_strings, (GFunc)g_free, NULL);
1299 g_list_free(umlclass->attributes_strings);
1301 umlclass->attributesbox_height = 2*0.1;
1303 umlclass->attributes_strings = NULL;
1304 if (g_list_length(umlclass->attributes) != 0)
1306 i = 0;
1307 list = umlclass->attributes;
1308 while (list != NULL)
1310 UMLAttribute *attr = (UMLAttribute *) list->data;
1311 gchar *attstr = uml_get_attribute_string(attr);
1313 umlclass->attributes_strings =
1314 g_list_append(umlclass->attributes_strings, attstr);
1316 if (attr->abstract)
1318 width = dia_font_string_width(attstr,
1319 umlclass->abstract_font,
1320 umlclass->abstract_font_height);
1321 umlclass->attributesbox_height += umlclass->abstract_font_height;
1323 else
1325 width = dia_font_string_width(attstr,
1326 umlclass->normal_font,
1327 umlclass->font_height);
1328 umlclass->attributesbox_height += umlclass->font_height;
1330 maxwidth = MAX(width, maxwidth);
1332 if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1334 int NumberOfLines = 0;
1335 gchar *Wrapped = uml_create_documentation_tag(attr->comment,
1336 umlclass->Comment_line_length,
1337 &NumberOfLines);
1339 width = dia_font_string_width(Wrapped,
1340 umlclass->comment_font,
1341 umlclass->comment_font_height);
1343 g_free(Wrapped);
1344 umlclass->attributesbox_height += (umlclass->comment_font_height * (NumberOfLines));
1345 umlclass->attributesbox_height += umlclass->comment_font_height/2;
1347 maxwidth = MAX(width, maxwidth);
1350 i++;
1351 list = g_list_next(list);
1355 if ((umlclass->attributesbox_height<0.4)|| umlclass->suppress_attributes )
1357 umlclass->attributesbox_height = 0.4;
1359 return maxwidth;
1364 ** umlclass_calculate_operation_data
1366 * FILENAME: \dia\objects\UML\class.c
1368 * PARAMETERS:
1369 * umlclass - The class to be drawn.
1371 * DESCRIPTION:
1372 * Calculate the bounding box for the operation. Include the
1373 * comments if enabled and present.
1375 * RETURNS:
1376 * The real width of the attribute bounding box.
1379 static real
1380 umlclass_calculate_operation_data(UMLClass *umlclass)
1382 int i;
1383 int pos_next_comma;
1384 int pos_brace;
1385 int wrap_pos;
1386 int last_wrap_pos;
1387 int ident;
1388 int offset;
1389 int maxlinewidth;
1390 int length;
1391 real maxwidth = 0.0;
1392 real width = 0.0;
1393 GList *list;
1394 GList *sublist;
1395 GList *wrapsublist;
1397 /* operations box: */
1398 umlclass->operationsbox_height = 2*0.1;
1399 /* neither leak previously calculated strings ... */
1400 if (umlclass->operations_strings != NULL)
1402 g_list_foreach(umlclass->operations_strings, (GFunc)g_free, NULL);
1403 g_list_free(umlclass->operations_strings);
1404 umlclass->operations_strings = NULL;
1406 /* ... nor their wrappings */
1407 if (umlclass->operations_wrappos != NULL)
1409 g_list_foreach(umlclass->operations_wrappos, (GFunc)g_list_free, NULL);
1410 g_list_free(umlclass->operations_wrappos);
1411 umlclass->operations_wrappos = NULL;
1414 if (0 != g_list_length(umlclass->operations))
1416 i = 0;
1417 list = umlclass->operations;
1418 while (list != NULL)
1420 UMLOperation *op = (UMLOperation *) list->data;
1421 gchar *opstr = uml_get_operation_string(op);
1423 umlclass->operations_strings =
1424 g_list_append(umlclass->operations_strings, opstr);
1426 length = 0;
1427 if( umlclass->wrap_operations )
1429 length = strlen( (const gchar*)opstr);
1430 sublist = NULL;
1431 if( length > umlclass->wrap_after_char)
1433 gchar *part_opstr;
1434 sublist = g_list_append( sublist, GINT_TO_POINTER( 1));
1436 /* count maximal line width to create a secure buffer (part_opstr)
1437 and build the sublist with the wrapping data for the current operation, which will be used by umlclass_draw(), too.
1438 The content of the sublist is:
1439 1st element: (bool) wrapping needed or not, 2nd: indentation in chars, 3rd-last: absolute wrapping positions */
1440 pos_next_comma = pos_brace = wrap_pos = offset = maxlinewidth = umlclass->max_wrapped_line_width = 0;
1441 while( wrap_pos + offset < length)
1445 pos_next_comma = strcspn( (const gchar*)opstr + wrap_pos + offset, ",");
1446 wrap_pos += pos_next_comma + 1;
1447 } while( wrap_pos < umlclass->wrap_after_char - pos_brace && wrap_pos + offset < length);
1449 if( offset == 0){
1450 pos_brace = strcspn( opstr, "(");
1451 sublist = g_list_append( sublist, GINT_TO_POINTER( pos_brace+1));
1453 sublist = g_list_append( sublist, GINT_TO_POINTER( wrap_pos + offset));
1455 maxlinewidth = MAX(maxlinewidth, wrap_pos);
1457 offset += wrap_pos;
1458 wrap_pos = 0;
1460 umlclass->max_wrapped_line_width = MAX( umlclass->max_wrapped_line_width, maxlinewidth+1);
1462 wrapsublist = g_list_next( sublist);
1463 ident = GPOINTER_TO_INT( wrapsublist->data);
1464 part_opstr = g_alloca(umlclass->max_wrapped_line_width+ident+1);
1465 pos_next_comma = pos_brace = wrap_pos = offset = 0;
1467 wrapsublist = g_list_next( wrapsublist);
1468 wrap_pos = last_wrap_pos = 0;
1470 while( wrapsublist != NULL){
1471 DiaFont *Font;
1472 real FontHeight;
1473 wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
1474 if( last_wrap_pos == 0){
1475 strncpy( part_opstr, opstr, wrap_pos);
1476 memset( part_opstr+wrap_pos, '\0', 1);
1478 else
1480 memset( part_opstr, ' ', ident);
1481 memset( part_opstr+ident, '\0', 1);
1482 strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
1485 switch(op->inheritance_type)
1487 case UML_ABSTRACT:
1488 Font = umlclass->abstract_font;
1489 FontHeight = umlclass->abstract_font_height;
1490 break;
1491 case UML_POLYMORPHIC:
1492 Font = umlclass->polymorphic_font;
1493 FontHeight = umlclass->polymorphic_font_height;
1494 break;
1495 case UML_LEAF:
1496 default:
1497 Font = umlclass->normal_font;
1498 FontHeight = umlclass->font_height;
1500 width = dia_font_string_width(part_opstr,Font,FontHeight);
1501 umlclass->operationsbox_height += FontHeight;
1503 maxwidth = MAX(width, maxwidth);
1504 last_wrap_pos = wrap_pos;
1505 wrapsublist = g_list_next( wrapsublist);
1508 else
1510 sublist = g_list_append( sublist, GINT_TO_POINTER( 0));
1512 umlclass->operations_wrappos = g_list_append( umlclass->operations_wrappos, sublist);
1515 if( !umlclass->wrap_operations || !(length > umlclass->wrap_after_char)) {
1516 DiaFont *Font;
1517 real FontHeight;
1519 switch(op->inheritance_type)
1521 case UML_ABSTRACT:
1522 Font = umlclass->abstract_font;
1523 FontHeight = umlclass->abstract_font_height;
1524 break;
1525 case UML_POLYMORPHIC:
1526 Font = umlclass->polymorphic_font;
1527 FontHeight = umlclass->polymorphic_font_height;
1528 break;
1529 case UML_LEAF:
1530 default:
1531 Font = umlclass->normal_font;
1532 FontHeight = umlclass->font_height;
1534 width = dia_font_string_width(opstr,Font,FontHeight);
1535 umlclass->operationsbox_height += FontHeight;
1537 maxwidth = MAX(width, maxwidth);
1540 if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1541 int NumberOfLines = 0;
1542 gchar *Wrapped = uml_create_documentation_tag(op->comment,
1543 umlclass->Comment_line_length,
1544 &NumberOfLines);
1546 width = dia_font_string_width(Wrapped,
1547 umlclass->comment_font,
1548 umlclass->comment_font_height);
1550 g_free(Wrapped);
1551 umlclass->operationsbox_height += (umlclass->comment_font_height * (NumberOfLines+1));
1553 maxwidth = MAX(width, maxwidth);
1556 i++;
1557 list = g_list_next(list);
1561 umlclass->element.width = maxwidth + 2*0.3;
1563 if ((umlclass->operationsbox_height<0.4) || umlclass->suppress_operations ) {
1564 umlclass->operationsbox_height = 0.4;
1567 return maxwidth;
1572 ** umlclass_calculate_data
1574 * FILENAME: \dia\objects\UML\class.c
1576 * PARAMETERS:
1577 * umlclass - The class to be drawn.
1579 * DESCRIPTION:
1580 * This function calculates the bounding box of the class image to be
1581 * displayed. It also calculates the three containing boxes. This is
1582 * done by calculating the size of the text to be displayed within
1583 * each of the contained bounding boxes, name, attributes and
1584 * operations.
1585 * Because the comments may require wrapping, each comment is wrapped
1586 * and the resulting number of lines is used to calculate the size of
1587 * the comment within the box.
1588 * The various font settings with in the class properties contribute
1589 * to the overall size of the resulting bounding box.
1591 * RETURNS:
1594 void
1595 umlclass_calculate_data(UMLClass *umlclass)
1597 int i;
1598 int pos_next_comma;
1599 int pos_brace;
1600 int wrap_pos;
1601 int last_wrap_pos;
1602 int ident;
1603 int offset;
1604 int maxlinewidth;
1605 int length;
1606 real maxwidth = 0.0;
1607 real width;
1608 GList *list;
1609 GList *sublist;
1610 GList *wrapsublist;
1613 if (!umlclass->destroyed)
1615 maxwidth = MAX(umlclass_calculate_name_data(umlclass), maxwidth);
1616 maxwidth = MAX(umlclass_calculate_attribute_data(umlclass), maxwidth);
1617 maxwidth = MAX(umlclass_calculate_operation_data(umlclass), maxwidth);
1619 umlclass->element.height = umlclass->namebox_height;
1620 umlclass->element.width = maxwidth+0.5;
1622 if (umlclass->visible_attributes){
1623 umlclass->element.height += umlclass->attributesbox_height;
1625 if (umlclass->visible_operations){
1626 umlclass->element.height += umlclass->operationsbox_height;
1628 /* templates box: */
1629 if (umlclass->templates_strings != NULL)
1631 for (i=0;i<umlclass->num_templates;i++)
1633 g_free(umlclass->templates_strings[i]);
1635 g_free(umlclass->templates_strings);
1637 umlclass->num_templates = g_list_length(umlclass->formal_params);
1639 umlclass->templates_height =
1640 umlclass->font_height * umlclass->num_templates + 2*0.1;
1641 umlclass->templates_height = MAX(umlclass->templates_height, 1.0);
1644 umlclass->templates_strings = NULL;
1645 maxwidth = 2.3;
1646 if (umlclass->num_templates != 0)
1648 umlclass->templates_strings =
1649 g_malloc (sizeof (gchar *) * umlclass->num_templates);
1650 i = 0;
1651 list = umlclass->formal_params;
1652 while (list != NULL)
1654 UMLFormalParameter *param;
1656 param = (UMLFormalParameter *) list->data;
1657 umlclass->templates_strings[i] = uml_get_formalparameter_string(param);
1659 width = dia_font_string_width(umlclass->templates_strings[i],
1660 umlclass->normal_font,
1661 umlclass->font_height);
1662 maxwidth = MAX(width, maxwidth);
1664 i++;
1665 list = g_list_next(list);
1668 umlclass->templates_width = maxwidth + 2*0.2;
1672 static void
1673 fill_in_fontdata(UMLClass *umlclass)
1675 if (umlclass->normal_font == NULL) {
1676 umlclass->font_height = 0.8;
1677 umlclass->normal_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, 0.8);
1679 if (umlclass->abstract_font == NULL) {
1680 umlclass->abstract_font_height = 0.8;
1681 umlclass->abstract_font =
1682 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD, 0.8);
1684 if (umlclass->polymorphic_font == NULL) {
1685 umlclass->polymorphic_font_height = 0.8;
1686 umlclass->polymorphic_font =
1687 dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC, 0.8);
1689 if (umlclass->classname_font == NULL) {
1690 umlclass->classname_font_height = 1.0;
1691 umlclass->classname_font =
1692 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD, 1.0);
1694 if (umlclass->abstract_classname_font == NULL) {
1695 umlclass->abstract_classname_font_height = 1.0;
1696 umlclass->abstract_classname_font =
1697 dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC, 1.0);
1699 if (umlclass->comment_font == NULL) {
1700 umlclass->comment_font_height = 0.7;
1701 umlclass->comment_font = dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1705 static DiaObject *
1706 umlclass_create(Point *startpoint,
1707 void *user_data,
1708 Handle **handle1,
1709 Handle **handle2)
1711 UMLClass *umlclass;
1712 Element *elem;
1713 DiaObject *obj;
1714 int i;
1716 umlclass = g_malloc0(sizeof(UMLClass));
1717 elem = &umlclass->element;
1718 obj = &elem->object;
1720 obj->type = &umlclass_type;
1722 obj->ops = &umlclass_ops;
1724 elem->corner = *startpoint;
1726 #ifdef UML_MAINPOINT
1727 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1); /* No attribs or ops => 0 extra connectionpoints. */
1728 #else
1729 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS); /* No attribs or ops => 0 extra connectionpoints. */
1730 #endif
1732 umlclass->properties_dialog = NULL;
1733 fill_in_fontdata(umlclass);
1735 umlclass->name = g_strdup (_("Class"));
1736 umlclass->stereotype = NULL;
1737 umlclass->comment = NULL;
1739 umlclass->abstract = FALSE;
1741 umlclass->suppress_attributes = FALSE;
1742 umlclass->suppress_operations = FALSE;
1744 umlclass->visible_attributes = TRUE;
1745 umlclass->visible_operations = TRUE;
1746 umlclass->visible_comments = FALSE;
1748 umlclass->wrap_operations = TRUE;
1749 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
1751 umlclass->attributes = NULL;
1753 umlclass->operations = NULL;
1755 umlclass->template = (GPOINTER_TO_INT(user_data)==1);
1756 umlclass->formal_params = NULL;
1758 umlclass->stereotype_string = NULL;
1759 umlclass->attributes_strings = NULL;
1760 umlclass->operations_strings = NULL;
1761 umlclass->operations_wrappos = NULL;
1762 umlclass->templates_strings = NULL;
1764 umlclass->text_color = color_black;
1765 umlclass->line_color = attributes_get_foreground();
1766 umlclass->fill_color = attributes_get_background();
1768 umlclass_calculate_data(umlclass);
1770 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1771 obj->connections[i] = &umlclass->connections[i];
1772 umlclass->connections[i].object = obj;
1773 umlclass->connections[i].connected = NULL;
1775 #ifdef UML_MAINPOINT
1776 /* Put mainpoint at the end, after conditional attr/oprn points,
1777 * but store it in the local connectionpoint array. */
1778 i += umlclass_num_dynamic_connectionpoints(umlclass);
1779 obj->connections[i] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
1780 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = obj;
1781 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1782 #endif
1784 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
1785 umlclass_update_data(umlclass);
1787 for (i=0;i<8;i++) {
1788 obj->handles[i]->type = HANDLE_NON_MOVABLE;
1791 *handle1 = NULL;
1792 *handle2 = NULL;
1793 return &umlclass->element.object;
1796 static void
1797 umlclass_destroy(UMLClass *umlclass)
1799 int i;
1800 GList *list;
1801 UMLAttribute *attr;
1802 UMLOperation *op;
1803 UMLFormalParameter *param;
1805 umlclass_sanity_check(umlclass, "Destroying");
1807 umlclass->destroyed = TRUE;
1809 dia_font_unref(umlclass->normal_font);
1810 dia_font_unref(umlclass->abstract_font);
1811 dia_font_unref(umlclass->polymorphic_font);
1812 dia_font_unref(umlclass->classname_font);
1813 dia_font_unref(umlclass->abstract_classname_font);
1814 dia_font_unref(umlclass->comment_font);
1816 element_destroy(&umlclass->element);
1818 g_free(umlclass->name);
1819 g_free(umlclass->stereotype);
1820 g_free(umlclass->comment);
1822 list = umlclass->attributes;
1823 while (list != NULL) {
1824 attr = (UMLAttribute *)list->data;
1825 printf("Destroying attr %p\n", attr);
1826 uml_attribute_destroy(attr);
1827 list = g_list_next(list);
1829 printf("Freeing umlclass->attributes %p\n", umlclass->attributes);
1830 g_list_free(umlclass->attributes);
1832 list = umlclass->operations;
1833 while (list != NULL) {
1834 op = (UMLOperation *)list->data;
1835 uml_operation_destroy(op);
1836 list = g_list_next(list);
1838 g_list_free(umlclass->operations);
1840 list = umlclass->formal_params;
1841 while (list != NULL) {
1842 param = (UMLFormalParameter *)list->data;
1843 uml_formalparameter_destroy(param);
1844 list = g_list_next(list);
1846 g_list_free(umlclass->formal_params);
1848 if (umlclass->stereotype_string != NULL) {
1849 g_free(umlclass->stereotype_string);
1852 if (umlclass->attributes_strings != NULL) {
1853 g_list_foreach(umlclass->attributes_strings, (GFunc)g_free, NULL);
1854 g_list_free(umlclass->attributes_strings);
1855 umlclass->attributes_strings = NULL;
1858 if (umlclass->operations_strings != NULL) {
1859 g_list_foreach(umlclass->operations_strings, (GFunc)g_free, NULL);
1860 g_list_free(umlclass->operations_strings);
1861 umlclass->operations_strings = NULL;
1864 if (umlclass->operations_wrappos != NULL) {
1865 g_list_foreach(umlclass->operations_wrappos, (GFunc)g_list_free, NULL);
1866 g_list_free(umlclass->operations_wrappos);
1867 umlclass->operations_wrappos = NULL;
1870 if (umlclass->templates_strings != NULL) {
1871 for (i=0;i<umlclass->num_templates;i++) {
1872 g_free(umlclass->templates_strings[i]);
1874 g_free(umlclass->templates_strings);
1877 if (umlclass->properties_dialog != NULL) {
1878 g_list_free(umlclass->properties_dialog->deleted_connections);
1879 gtk_widget_destroy(umlclass->properties_dialog->dialog);
1880 g_free(umlclass->properties_dialog);
1884 static DiaObject *
1885 umlclass_copy(UMLClass *umlclass)
1887 int i;
1888 UMLClass *newumlclass;
1889 Element *elem, *newelem;
1890 DiaObject *newobj;
1891 GList *list;
1892 UMLAttribute *attr;
1893 UMLAttribute *newattr;
1894 UMLOperation *op;
1895 UMLOperation *newop;
1896 UMLFormalParameter *param;
1898 elem = &umlclass->element;
1900 newumlclass = g_malloc0(sizeof(UMLClass));
1901 newelem = &newumlclass->element;
1902 newobj = &newelem->object;
1904 element_copy(elem, newelem);
1906 newumlclass->font_height = umlclass->font_height;
1907 newumlclass->abstract_font_height = umlclass->abstract_font_height;
1908 newumlclass->polymorphic_font_height = umlclass->polymorphic_font_height;
1909 newumlclass->classname_font_height = umlclass->classname_font_height;
1910 newumlclass->abstract_classname_font_height =
1911 umlclass->abstract_classname_font_height;
1912 newumlclass->comment_font_height =
1913 umlclass->comment_font_height;
1915 newumlclass->normal_font =
1916 dia_font_ref(umlclass->normal_font);
1917 newumlclass->abstract_font =
1918 dia_font_ref(umlclass->abstract_font);
1919 newumlclass->polymorphic_font =
1920 dia_font_ref(umlclass->polymorphic_font);
1921 newumlclass->classname_font =
1922 dia_font_ref(umlclass->classname_font);
1923 newumlclass->abstract_classname_font =
1924 dia_font_ref(umlclass->abstract_classname_font);
1925 newumlclass->comment_font =
1926 dia_font_ref(umlclass->comment_font);
1928 newumlclass->name = g_strdup(umlclass->name);
1929 if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0')
1930 newumlclass->stereotype = g_strdup(umlclass->stereotype);
1931 else
1932 newumlclass->stereotype = NULL;
1934 if (umlclass->comment != NULL)
1935 newumlclass->comment = g_strdup(umlclass->comment);
1936 else
1937 newumlclass->comment = NULL;
1939 newumlclass->abstract = umlclass->abstract;
1940 newumlclass->suppress_attributes = umlclass->suppress_attributes;
1941 newumlclass->suppress_operations = umlclass->suppress_operations;
1942 newumlclass->visible_attributes = umlclass->visible_attributes;
1943 newumlclass->visible_operations = umlclass->visible_operations;
1944 newumlclass->visible_comments = umlclass->visible_comments;
1945 newumlclass->wrap_operations = umlclass->wrap_operations;
1946 newumlclass->wrap_after_char = umlclass->wrap_after_char;
1947 newumlclass->Comment_line_length = umlclass->Comment_line_length;
1948 newumlclass->text_color = umlclass->text_color;
1949 newumlclass->line_color = umlclass->line_color;
1950 newumlclass->fill_color = umlclass->fill_color;
1952 newumlclass->attributes = NULL;
1953 list = umlclass->attributes;
1954 while (list != NULL) {
1955 attr = (UMLAttribute *)list->data;
1956 newattr = uml_attribute_copy(attr, newobj);
1958 newumlclass->attributes = g_list_prepend(newumlclass->attributes,
1959 newattr);
1960 list = g_list_next(list);
1963 newumlclass->operations = NULL;
1964 list = umlclass->operations;
1965 while (list != NULL) {
1966 op = (UMLOperation *)list->data;
1967 newop = uml_operation_copy(op);
1968 newop->left_connection->object = newobj;
1969 newop->left_connection->connected = NULL;
1971 newop->right_connection->object = newobj;
1972 newop->right_connection->connected = NULL;
1974 newumlclass->operations = g_list_prepend(newumlclass->operations,
1975 newop);
1976 list = g_list_next(list);
1979 newumlclass->template = umlclass->template;
1981 newumlclass->formal_params = NULL;
1982 list = umlclass->formal_params;
1983 while (list != NULL) {
1984 param = (UMLFormalParameter *)list->data;
1985 newumlclass->formal_params =
1986 g_list_prepend(newumlclass->formal_params,
1987 uml_formalparameter_copy(param));
1988 list = g_list_next(list);
1991 newumlclass->properties_dialog = NULL;
1993 newumlclass->stereotype_string = NULL;
1994 newumlclass->attributes_strings = NULL;
1995 newumlclass->operations_strings = NULL;
1996 newumlclass->operations_wrappos = NULL;
1997 newumlclass->templates_strings = NULL;
1999 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2000 newobj->connections[i] = &newumlclass->connections[i];
2001 newumlclass->connections[i].object = newobj;
2002 newumlclass->connections[i].connected = NULL;
2003 newumlclass->connections[i].pos = umlclass->connections[i].pos;
2004 newumlclass->connections[i].last_pos = umlclass->connections[i].last_pos;
2007 umlclass_calculate_data(newumlclass);
2009 i = UMLCLASS_CONNECTIONPOINTS;
2010 if ( (newumlclass->visible_attributes) &&
2011 (!newumlclass->suppress_attributes)) {
2012 list = newumlclass->attributes;
2013 while (list != NULL) {
2014 attr = (UMLAttribute *)list->data;
2015 printf("Setting copy conn %d of %p to left %p\n", i, newobj, attr->left_connection);
2016 newobj->connections[i++] = attr->left_connection;
2017 printf("Setting copy conn %d of %p to right %p\n", i, newobj, attr->right_connection);
2018 newobj->connections[i++] = attr->right_connection;
2020 list = g_list_next(list);
2024 if ( (newumlclass->visible_operations) &&
2025 (!newumlclass->suppress_operations)) {
2026 list = newumlclass->operations;
2027 while (list != NULL) {
2028 op = (UMLOperation *)list->data;
2029 newobj->connections[i++] = op->left_connection;
2030 newobj->connections[i++] = op->right_connection;
2032 list = g_list_next(list);
2036 #ifdef UML_MAINPOINT
2037 newobj->connections[i] = &newumlclass->connections[UMLCLASS_CONNECTIONPOINTS];
2038 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = newobj;
2039 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
2040 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos =
2041 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos;
2042 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos =
2043 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos;
2044 newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags =
2045 umlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags;
2046 i++;
2047 #endif
2049 umlclass_update_data(newumlclass);
2051 umlclass_sanity_check(newumlclass, "Copied");
2053 return &newumlclass->element.object;
2057 static void
2058 umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
2059 const char *filename)
2061 UMLAttribute *attr;
2062 UMLOperation *op;
2063 UMLFormalParameter *formal_param;
2064 GList *list;
2065 AttributeNode attr_node;
2067 umlclass_sanity_check(umlclass, "Saving");
2069 element_save(&umlclass->element, obj_node);
2071 /* Class info: */
2072 data_add_string(new_attribute(obj_node, "name"),
2073 umlclass->name);
2074 data_add_string(new_attribute(obj_node, "stereotype"),
2075 umlclass->stereotype);
2076 data_add_string(new_attribute(obj_node, "comment"),
2077 umlclass->comment);
2078 data_add_boolean(new_attribute(obj_node, "abstract"),
2079 umlclass->abstract);
2080 data_add_boolean(new_attribute(obj_node, "suppress_attributes"),
2081 umlclass->suppress_attributes);
2082 data_add_boolean(new_attribute(obj_node, "suppress_operations"),
2083 umlclass->suppress_operations);
2084 data_add_boolean(new_attribute(obj_node, "visible_attributes"),
2085 umlclass->visible_attributes);
2086 data_add_boolean(new_attribute(obj_node, "visible_operations"),
2087 umlclass->visible_operations);
2088 data_add_boolean(new_attribute(obj_node, "visible_comments"),
2089 umlclass->visible_comments);
2090 data_add_boolean(new_attribute(obj_node, "wrap_operations"),
2091 umlclass->wrap_operations);
2092 data_add_int(new_attribute(obj_node, "wrap_after_char"),
2093 umlclass->wrap_after_char);
2094 data_add_int(new_attribute(obj_node, "Comment_line_length"),
2095 umlclass->Comment_line_length);
2096 data_add_color(new_attribute(obj_node, "line_color"),
2097 &umlclass->line_color);
2098 data_add_color(new_attribute(obj_node, "fill_color"),
2099 &umlclass->fill_color);
2100 data_add_color(new_attribute(obj_node, "text_color"),
2101 &umlclass->text_color);
2102 data_add_font (new_attribute (obj_node, "normal_font"),
2103 umlclass->normal_font);
2104 data_add_font (new_attribute (obj_node, "abstract_font"),
2105 umlclass->abstract_font);
2106 data_add_font (new_attribute (obj_node, "polymorphic_font"),
2107 umlclass->polymorphic_font);
2108 data_add_font (new_attribute (obj_node, "classname_font"),
2109 umlclass->classname_font);
2110 data_add_font (new_attribute (obj_node, "abstract_classname_font"),
2111 umlclass->abstract_classname_font);
2112 data_add_font (new_attribute (obj_node, "comment_font"),
2113 umlclass->comment_font);
2114 data_add_real (new_attribute (obj_node, "normal_font_height"),
2115 umlclass->font_height);
2116 data_add_real (new_attribute (obj_node, "polymorphic_font_height"),
2117 umlclass->polymorphic_font_height);
2118 data_add_real (new_attribute (obj_node, "abstract_font_height"),
2119 umlclass->abstract_font_height);
2120 data_add_real (new_attribute (obj_node, "classname_font_height"),
2121 umlclass->classname_font_height);
2122 data_add_real (new_attribute (obj_node, "abstract_classname_font_height"),
2123 umlclass->abstract_classname_font_height);
2124 data_add_real (new_attribute (obj_node, "comment_font_height"),
2125 umlclass->comment_font_height);
2127 /* Attribute info: */
2128 attr_node = new_attribute(obj_node, "attributes");
2129 list = umlclass->attributes;
2130 while (list != NULL) {
2131 attr = (UMLAttribute *) list->data;
2132 printf("Writing attr %p\n", attr);
2133 uml_attribute_write(attr_node, attr);
2134 list = g_list_next(list);
2137 /* Operations info: */
2138 attr_node = new_attribute(obj_node, "operations");
2139 list = umlclass->operations;
2140 while (list != NULL) {
2141 op = (UMLOperation *) list->data;
2142 uml_operation_write(attr_node, op);
2143 list = g_list_next(list);
2146 /* Template info: */
2147 data_add_boolean(new_attribute(obj_node, "template"),
2148 umlclass->template);
2150 attr_node = new_attribute(obj_node, "templates");
2151 list = umlclass->formal_params;
2152 while (list != NULL) {
2153 formal_param = (UMLFormalParameter *) list->data;
2154 uml_formalparameter_write(attr_node, formal_param);
2155 list = g_list_next(list);
2159 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
2160 const char *filename)
2162 UMLClass *umlclass;
2163 Element *elem;
2164 DiaObject *obj;
2165 AttributeNode attr_node;
2166 int i;
2167 GList *list;
2170 umlclass = g_malloc0(sizeof(UMLClass));
2171 elem = &umlclass->element;
2172 obj = &elem->object;
2174 obj->type = &umlclass_type;
2175 obj->ops = &umlclass_ops;
2177 element_load(elem, obj_node);
2179 #ifdef UML_MAINPOINT
2180 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1);
2181 #else
2182 element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS);
2183 #endif
2185 umlclass->properties_dialog = NULL;
2187 for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2188 obj->connections[i] = &umlclass->connections[i];
2189 umlclass->connections[i].object = obj;
2190 umlclass->connections[i].connected = NULL;
2193 fill_in_fontdata(umlclass);
2195 /* kind of dirty, object_load_props() may leave us in an inconsnected = NULL;
2198 fill_in_fontdata(umlclass);
2200 /* kind of dirty, object_load_props() may leave us in an inconsistent state --hb */
2201 object_load_props(obj,obj_node);
2202 /* a bunch of properties still need their own special handling */
2204 /* Class info: */
2206 /* parameters loaded via StdProp dont belong here anymore. In case of strings they
2207 * will produce leaks. Otherwise the are just wasteing time (at runtime and while
2208 * reading the code). Except maybe for some compatibility stuff.
2209 * Although that *could* probably done via StdProp too. --hb
2212 /* new since 0.94, don't wrap by default to keep old diagrams intact */
2213 umlclass->wrap_operations = FALSE;
2214 attr_node = object_find_attribute(obj_node, "wrap_operations");
2215 if (attr_node != NULL)
2216 umlclass->wrap_operations = data_boolean(attribute_first_data(attr_node));
2218 umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
2219 attr_node = object_find_attribute(obj_node, "wrap_after_char");
2220 if (attr_node != NULL)
2221 umlclass->wrap_after_char = data_int(attribute_first_data(attr_node));
2223 umlclass->Comment_line_length = UMLCLASS_COMMENT_LINE_LENGTH;
2224 attr_node = object_find_attribute(obj_node,"Comment_line_length");
2225 if (attr_node != NULL)
2227 umlclass->Comment_line_length = data_int(attribute_first_data(attr_node));
2229 umlclass->line_color = color_black;
2230 /* support the old name ... */
2231 attr_node = object_find_attribute(obj_node, "foreground_color");
2232 if(attr_node != NULL)
2233 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2234 umlclass->text_color = umlclass->line_color;
2235 /* ... but prefer the new one */
2236 attr_node = object_find_attribute(obj_node, "line_color");
2237 if(attr_node != NULL)
2238 data_color(attribute_first_data(attr_node), &umlclass->line_color);
2240 umlclass->fill_color = color_white;
2241 /* support the old name ... */
2242 attr_node = object_find_attribute(obj_node, "background_color");
2243 if(attr_node != NULL)
2244 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2245 /* ... but prefer the new one */
2246 attr_node = object_find_attribute(obj_node, "fill_color");
2247 if(attr_node != NULL)
2248 data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2250 /* Attribute info: */
2251 list = umlclass->attributes;
2252 while (list) {
2253 UMLAttribute *attr = list->data;
2254 g_assert(attr);
2256 attr->left_connection->object = obj;
2257 attr->left_connection->connected = NULL;
2258 attr->right_connection->object = obj;
2259 attr->right_connection->connected = NULL;
2260 list = g_list_next(list);
2263 /* Operations info: */
2264 list = umlclass->operations;
2265 while (list) {
2266 UMLOperation *op = (UMLOperation *)list->data;
2267 g_assert(op);
2269 op->left_connection->object = obj;
2270 op->left_connection->connected = NULL;
2272 op->right_connection->object = obj;
2273 op->right_connection->connected = NULL;
2274 list = g_list_next(list);
2277 /* Template info: */
2278 umlclass->template = FALSE;
2279 attr_node = object_find_attribute(obj_node, "template");
2280 if (attr_node != NULL)
2281 umlclass->template = data_boolean(attribute_first_data(attr_node));
2283 fill_in_fontdata(umlclass);
2285 umlclass->stereotype_string = NULL;
2286 umlclass->attributes_strings = NULL;
2287 umlclass->operations_strings = NULL;
2288 umlclass->operations_wrappos = NULL;
2289 umlclass->templates_strings = NULL;
2291 umlclass_calculate_data(umlclass);
2293 elem->extra_spacing.border_trans = UMLCLASS_BORDER/2.0;
2294 umlclass_update_data(umlclass);
2296 for (i=0;i<8;i++) {
2297 obj->handles[i]->type = HANDLE_NON_MOVABLE;
2300 umlclass_sanity_check(umlclass, "Loaded class");
2302 return &umlclass->element.object;
2305 /** Returns the number of connection points used by the attributes and
2306 * connections in the current state of the object.
2308 static int
2309 umlclass_num_dynamic_connectionpoints(UMLClass *umlclass) {
2310 int num = 0;
2311 if ( (umlclass->visible_attributes) &&
2312 (!umlclass->suppress_attributes)) {
2313 GList *list = umlclass->attributes;
2314 num += 2 * g_list_length(list);
2317 if ( (umlclass->visible_operations) &&
2318 (!umlclass->suppress_operations)) {
2319 GList *list = umlclass->operations;
2320 num += 2 * g_list_length(list);
2322 return num;
2325 void
2326 umlclass_sanity_check(UMLClass *c, gchar *msg)
2328 #ifdef UML_MAINPOINT
2329 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS + 1;
2330 #else
2331 int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS;
2332 #endif
2333 DiaObject *obj = (DiaObject*)c;
2334 GList *attrs, *ops;
2335 int i;
2337 dia_object_sanity_check((DiaObject *)c, msg);
2339 /* Check that num_connections is correct */
2340 dia_assert_true(num_fixed_connections + umlclass_num_dynamic_connectionpoints(c)
2341 == obj->num_connections,
2342 "%s: Class %p has %d connections, but %d fixed and %d dynamic\n",
2343 msg, c, obj->num_connections, num_fixed_connections,
2344 umlclass_num_dynamic_connectionpoints(c));
2346 for (i = 0; i < UMLCLASS_CONNECTIONPOINTS; i++) {
2347 dia_assert_true(&c->connections[i] == obj->connections[i],
2348 "%s: Class %p connection mismatch at %d: %p != %p\n",
2349 msg, c, i, &c->connections[i], obj->connections[i]);
2352 #ifdef UML_MAINPOINT
2353 dia_assert_true(&c->connections[i] ==
2354 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2355 "%s: Class %p mainpoint mismatch: %p != %p (at %d)\n",
2356 msg, c, i, &c->connections[i],
2357 obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2358 i + umlclass_num_dynamic_connectionpoints(c));
2359 #endif
2361 /* Check that attributes are set up right. */
2362 i = 0;
2363 for (attrs = c->attributes; attrs != NULL; attrs = g_list_next(attrs)) {
2364 UMLAttribute *attr = (UMLAttribute *)attrs->data;
2365 int conn_offset = UMLCLASS_CONNECTIONPOINTS + 2 * i;
2366 dia_assert_true(attr->name != NULL,
2367 "%s: %p attr %d has null name\n",
2368 msg, c, i);
2369 dia_assert_true(attr->type != NULL,
2370 "%s: %p attr %d has null type\n",
2371 msg, c, i);
2372 dia_assert_true(attr->comment != NULL,
2373 "%s: %p attr %d has null comment\n",
2374 msg, c, i);
2376 dia_assert_true(attr->left_connection != NULL,
2377 "%s: %p attr %d has null left connection\n",
2378 msg, c, i);
2379 dia_assert_true(attr->left_connection == obj->connections[conn_offset],
2380 "%s: %p attr %d left conn %p doesn't match obj conn %d: %p\n",
2381 msg, c, i, attr->left_connection,
2382 conn_offset, obj->connections[conn_offset]);
2383 dia_assert_true(attr->right_connection == obj->connections[conn_offset + 1],
2384 "%s: %p attr %d right conn %p doesn't match obj conn %d: %p\n",
2385 msg, c, i, attr->right_connection,
2386 conn_offset + 1, obj->connections[conn_offset + 1]);
2387 dia_assert_true(attr->right_connection != NULL,
2388 "%s: %p attr %d has null right connection\n",
2389 msg, c, i);
2391 i++;
2393 /* Check that operations are set up right. */