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.
20 * Purpose: This file contains implementation of the "class" code.
34 #include "diarenderer.h"
35 #include "attributes.h"
36 #include "properties.h"
41 #include "pixmaps/umlclass.xpm"
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
,
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
95 DiaObjectType umlclass_type
=
97 "UML - Class", /* name */
99 (char **) umlclass_xpm
, /* pixmap */
101 ¨class_type_ops
, /* ops */
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 */ },
209 static PropDescription
*
210 umlclass_describe_props(UMLClass
*umlclass
)
212 if (umlclass_props
[0].quark
== 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
= ¨attribute_extra
;
222 else if (0 == strcmp(umlclass_props
[i
].name
, "operations")) {
223 PropDescription
*records
= umloperation_extra
.common
.record
;
226 umlclass_props
[i
].extra_data
= ¨operation_extra
;
227 while (records
[j
].name
!= NULL
) {
228 if (0 == strcmp(records
[j
].name
, "parameters"))
229 records
[j
].extra_data
= ¨parameter_extra
;
233 else if (0 == strcmp(umlclass_props
[i
].name
, "templates"))
234 umlclass_props
[i
].extra_data
= ¨formalparameter_extra
;
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
) } ,
290 umlclass_get_props(UMLClass
* umlclass
, GPtrArray
*props
)
292 object_get_props_from_offsets(¨class
->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
= {
303 sizeof(umlclass_menu_items
)/sizeof(DiaMenuItem
),
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 ¨class_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
);
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
= ¨class
->element
.object
;
339 object_set_props_from_offsets(¨class
->element
.object
, umlclass_offsets
,
342 num
= UMLCLASS_CONNECTIONPOINTS
+ umlclass_num_dynamic_connectionpoints(umlclass
);
345 obj
->num_connections
= num
+ 1;
347 obj
->num_connections
= num
;
350 obj
->connections
= g_realloc(obj
->connections
, obj
->num_connections
*sizeof(ConnectionPoint
*));
353 if (num
> UMLCLASS_CONNECTIONPOINTS
) {
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
;
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
;
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
;
379 obj
->connections
[i
] = op
->right_connection
;
380 obj
->connections
[i
]->object
= obj
;
382 list
= g_list_next(list
);
386 obj
->connections
[num
] = ¨class
->connections
[UMLCLASS_CONNECTIONPOINTS
];
387 obj
->connections
[num
]->object
= obj
;
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");
398 umlclass_distance_from(UMLClass
*umlclass
, Point
*point
)
400 DiaObject
*obj
= ¨class
->element
.object
;
401 return distance_rectangle_point(&obj
->bounding_box
, point
);
405 umlclass_select(UMLClass
*umlclass
, Point
*clicked_point
,
406 DiaRenderer
*interactive_renderer
)
408 element_update_handles(¨class
->element
);
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
);
420 assert(handle
->id
< UMLCLASS_CONNECTIONPOINTS
);
426 umlclass_move(UMLClass
*umlclass
, Point
*to
)
428 umlclass
->element
.corner
= *to
;
429 umlclass_update_data(umlclass
);
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
453 uml_underline_text(DiaRenderer
*renderer
,
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
492 * Finally, since this function returns newly created dynamic
493 * memory the caller must free the memory to prevent memory
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
502 * This function should most likely be move to a source file for
503 * handling global UML functionallity at some point.
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
);
521 /* Remove leading whitespace */
522 while( isspace(comment
[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
541 BytesToNextNewLine
= (Nl
- &comment
[CommentIndex
]);
544 if ((Nl
!= NULL
) && (BytesToNextNewLine
< LineLen
)){
545 LineLen
= BytesToNextNewLine
;
548 if( (CommentIndex
+ LineLen
) > LengthOfComment
){
549 LineLen
= LengthOfComment
-CommentIndex
;
552 if ((LineLen
== strlen(&comment
[CommentIndex
])) ||
553 isspace(comment
[CommentIndex
+LineLen
])){
559 if ((*NumberOfLines
> 1) &&( 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
])){
576 if (CommentIndex
< LengthOfComment
){
577 /* if this is not the last line add a new-line*/
578 strcat(WrappedComment
,"\n");
581 LengthOfWrappedComment
= strlen(WrappedComment
);
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
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
609 uml_draw_comments(DiaRenderer
*renderer
,
614 gint Comment_line_length
,
618 gint NumberOfLines
= 0;
620 gchar
*CommentString
= 0;
621 gchar
*NewLineP
= NULL
;
624 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
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
)
638 renderer_ops
->draw_string(renderer
, RenderP
, p
, alignment
, text_color
);
640 if ( NewLineP
== NULL
){
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.
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
668 umlclass_draw_namebox(UMLClass
*umlclass
, DiaRenderer
*renderer
, Element
*elem
)
670 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
674 Point LowerRightPoint
;
676 Color
*text_color
= ¨class
->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
693 renderer_ops
->fill_rect(renderer
, &StartPoint
, &LowerRightPoint
, ¨class
->fill_color
);
694 renderer_ops
->draw_rect(renderer
, &StartPoint
, &LowerRightPoint
, ¨class
->line_color
);
696 /* Start at the midpoint on the X axis */
697 StartPoint
.x
+= elem
->width
/ 2.0;
700 if (umlclass
->stereotype
!= NULL
&& umlclass
->stereotype
[0] != '\0') {
701 gchar
*String
= umlclass
->stereotype_string
;
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
);
709 if (umlclass
->name
!= NULL
) {
710 if (umlclass
->abstract
) {
711 font
= umlclass
->abstract_classname_font
;
712 font_height
= umlclass
->abstract_classname_font_height
;
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
);
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 ¨class
->text_color
, umlclass
->comment
,
727 umlclass
->Comment_line_length
, &StartPoint
, ALIGN_CENTER
);
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
752 umlclass_draw_attributebox(UMLClass
*umlclass
, DiaRenderer
*renderer
, Element
*elem
, real Yoffset
)
754 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
759 Color
*fill_color
= ¨class
->fill_color
;
760 Color
*line_color
= ¨class
->line_color
;
761 Color
*text_color
= ¨class
->text_color
;
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
) {
777 StartPoint
.x
+= (UMLCLASS_BORDER
/2.0 + 0.1);
780 list
= umlclass
->attributes
;
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
;
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 ¨class
->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
);
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
836 umlclass_draw_operationbox(UMLClass
*umlclass
, DiaRenderer
*renderer
, Element
*elem
, real Yoffset
)
838 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
844 Color
*fill_color
= ¨class
->fill_color
;
845 Color
*line_color
= ¨class
->line_color
;
846 Color
*text_color
= ¨class
->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
) {
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);
870 list
= umlclass
->operations
;
871 while (list
!= NULL
) {
872 UMLOperation
*op
= (UMLOperation
*)list
->data
;
876 switch (op
->inheritance_type
) {
878 font
= umlclass
->abstract_font
;
879 font_height
= umlclass
->abstract_font_height
;
881 case UML_POLYMORPHIC
:
882 font
= umlclass
->polymorphic_font
;
883 font_height
= umlclass
->polymorphic_font_height
;
887 font
= umlclass
->normal_font
;
888 font_height
= umlclass
->font_height
;
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);
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
);
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 ¨class
->text_color
, op
->comment
,
953 umlclass
->Comment_line_length
, &StartPoint
, ALIGN_LEFT
);
956 list
= g_list_next(list
);
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.
971 * At this time there is no provision for adding comments or documentation
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
980 umlclass_draw_template_parameters_box(UMLClass
*umlclass
, DiaRenderer
*renderer
, Element
*elem
)
982 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
988 DiaFont
*font
= umlclass
->normal_font
;
989 real font_height
= umlclass
->font_height
;
990 Color
*fill_color
= ¨class
->fill_color
;
991 Color
*line_color
= ¨class
->line_color
;
992 Color
*text_color
= ¨class
->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
);
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
);
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
1039 umlclass_draw(UMLClass
*umlclass
, DiaRenderer
*renderer
)
1041 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
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
= ¨class
->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
);
1067 umlclass_update_data(UMLClass
*umlclass
)
1069 Element
*elem
= ¨class
->element
;
1070 DiaObject
*obj
= &elem
->object
;
1075 int lowerleftcorner
;
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
;
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.
1209 umlclass_calculate_name_data(UMLClass
*umlclass
)
1211 real maxwidth
= 0.0;
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
);
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
,
1238 width
= dia_font_string_width (umlclass
->stereotype_string
,
1239 umlclass
->normal_font
,
1240 umlclass
->font_height
);
1241 maxwidth
= MAX(width
, maxwidth
);
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
);
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
1271 umlclass_calculate_attribute_data(UMLClass
*umlclass
)
1280 real maxwidth
= 0.0;
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)
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
);
1307 width
= dia_font_string_width(attstr
,
1308 umlclass
->abstract_font
,
1309 umlclass
->abstract_font_height
);
1310 umlclass
->attributesbox_height
+= umlclass
->abstract_font_height
;
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
,
1328 width
= dia_font_string_width(Wrapped
,
1329 umlclass
->comment_font
,
1330 umlclass
->comment_font_height
);
1333 umlclass
->attributesbox_height
+= (umlclass
->comment_font_height
* (NumberOfLines
));
1334 umlclass
->attributesbox_height
+= umlclass
->comment_font_height
/2;
1336 maxwidth
= MAX(width
, maxwidth
);
1340 list
= g_list_next(list
);
1344 if ((umlclass
->attributesbox_height
<0.4)|| umlclass
->suppress_attributes
)
1346 umlclass
->attributesbox_height
= 0.4;
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
1360 umlclass_calculate_operation_data(UMLClass
*umlclass
)
1371 real maxwidth
= 0.0;
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
))
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
);
1407 if( umlclass
->wrap_operations
)
1409 length
= strlen( (const gchar
*)opstr
);
1411 if( length
> umlclass
->wrap_after_char
)
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
);
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
);
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
){
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);
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
)
1468 Font
= umlclass
->abstract_font
;
1469 FontHeight
= umlclass
->abstract_font_height
;
1471 case UML_POLYMORPHIC
:
1472 Font
= umlclass
->polymorphic_font
;
1473 FontHeight
= umlclass
->polymorphic_font_height
;
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
);
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
)) {
1499 switch(op
->inheritance_type
)
1502 Font
= umlclass
->abstract_font
;
1503 FontHeight
= umlclass
->abstract_font_height
;
1505 case UML_POLYMORPHIC
:
1506 Font
= umlclass
->polymorphic_font
;
1507 FontHeight
= umlclass
->polymorphic_font_height
;
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
,
1526 width
= dia_font_string_width(Wrapped
,
1527 umlclass
->comment_font
,
1528 umlclass
->comment_font_height
);
1531 umlclass
->operationsbox_height
+= (umlclass
->comment_font_height
* (NumberOfLines
+1));
1533 maxwidth
= MAX(width
, maxwidth
);
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;
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
1563 umlclass_calculate_data(UMLClass
*umlclass
)
1574 real maxwidth
= 0.0;
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
;
1614 if (umlclass
->num_templates
!= 0)
1616 umlclass
->templates_strings
=
1617 g_malloc (sizeof (gchar
*) * umlclass
->num_templates
);
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
);
1633 list
= g_list_next(list
);
1636 umlclass
->templates_width
= maxwidth
+ 2*0.2;
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
1688 * This function should most likely be move to a source file for
1689 * handling global UML functionallity at some point.
1693 umlclass_create(Point
*startpoint
,
1703 umlclass
= g_malloc0(sizeof(UMLClass
));
1704 elem
= ¨class
->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. */
1713 element_init(elem
, 8, UMLCLASS_CONNECTIONPOINTS
); /* No attribs or ops => 0 extra connectionpoints. */
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"));
1730 umlclass
->name
= g_strdup (_("Class"));
1732 obj
->type
= ¨class_type
;
1733 obj
->ops
= ¨class_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
] = ¨class
->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
] = ¨class
->connections
[UMLCLASS_CONNECTIONPOINTS
];
1778 umlclass
->connections
[UMLCLASS_CONNECTIONPOINTS
].object
= obj
;
1779 umlclass
->connections
[UMLCLASS_CONNECTIONPOINTS
].connected
= NULL
;
1782 elem
->extra_spacing
.border_trans
= UMLCLASS_BORDER
/2.0;
1783 umlclass_update_data(umlclass
);
1786 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
1791 return ¨class
->element
.object
;
1795 umlclass_destroy(UMLClass
*umlclass
)
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(¨class
->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
);
1883 umlclass_copy(UMLClass
*umlclass
)
1886 UMLClass
*newumlclass
;
1887 Element
*elem
, *newelem
;
1891 UMLAttribute
*newattr
;
1893 UMLOperation
*newop
;
1894 UMLFormalParameter
*param
;
1896 elem
= ¨class
->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
);
1930 newumlclass
->stereotype
= NULL
;
1932 if (umlclass
->comment
!= NULL
)
1933 newumlclass
->comment
= g_strdup(umlclass
->comment
);
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
,
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
,
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
;
2047 umlclass_update_data(newumlclass
);
2049 umlclass_sanity_check(newumlclass
, "Copied");
2051 return &newumlclass
->element
.object
;
2056 umlclass_save(UMLClass
*umlclass
, ObjectNode obj_node
,
2057 const char *filename
)
2061 UMLFormalParameter
*formal_param
;
2063 AttributeNode attr_node
;
2065 umlclass_sanity_check(umlclass
, "Saving");
2067 element_save(¨class
->element
, obj_node
);
2070 data_add_string(new_attribute(obj_node
, "name"),
2072 data_add_string(new_attribute(obj_node
, "stereotype"),
2073 umlclass
->stereotype
);
2074 data_add_string(new_attribute(obj_node
, "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 ¨class
->line_color
);
2096 data_add_color(new_attribute(obj_node
, "fill_color"),
2097 ¨class
->fill_color
);
2098 data_add_color(new_attribute(obj_node
, "text_color"),
2099 ¨class
->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
)
2163 AttributeNode attr_node
;
2168 umlclass
= g_malloc0(sizeof(UMLClass
));
2169 elem
= ¨class
->element
;
2170 obj
= &elem
->object
;
2172 obj
->type
= ¨class_type
;
2173 obj
->ops
= ¨class_ops
;
2175 element_load(elem
, obj_node
);
2177 #ifdef UML_MAINPOINT
2178 element_init(elem
, 8, UMLCLASS_CONNECTIONPOINTS
+ 1);
2180 element_init(elem
, 8, UMLCLASS_CONNECTIONPOINTS
);
2183 umlclass
->properties_dialog
= NULL
;
2185 for (i
=0;i
<UMLCLASS_CONNECTIONPOINTS
;i
++) {
2186 obj
->connections
[i
] = ¨class
->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 */
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
), ¨class
->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
), ¨class
->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
), ¨class
->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
), ¨class
->fill_color
);
2248 /* Attribute info: */
2249 list
= umlclass
->attributes
;
2251 UMLAttribute
*attr
= list
->data
;
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
;
2264 UMLOperation
*op
= (UMLOperation
*)list
->data
;
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
);
2295 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
2298 umlclass_sanity_check(umlclass
, "Loaded class");
2300 return ¨class
->element
.object
;
2303 /** Returns the number of connection points used by the attributes and
2304 * connections in the current state of the object.
2307 umlclass_num_dynamic_connectionpoints(UMLClass
*umlclass
) {
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
);
2324 umlclass_sanity_check(UMLClass
*c
, gchar
*msg
)
2326 #ifdef UML_MAINPOINT
2327 int num_fixed_connections
= UMLCLASS_CONNECTIONPOINTS
+ 1;
2329 int num_fixed_connections
= UMLCLASS_CONNECTIONPOINTS
;
2331 DiaObject
*obj
= (DiaObject
*)c
;
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
));
2359 /* Check that attributes are set up right. */
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",
2367 dia_assert_true(attr
->type
!= NULL
,
2368 "%s: %p attr %d has null type\n",
2370 dia_assert_true(attr
->comment
!= NULL
,
2371 "%s: %p attr %d has null comment\n",
2374 dia_assert_true(attr
->left_connection
!= NULL
,
2375 "%s: %p attr %d has null left connection\n",
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",
2391 /* Check that operations are set up right. */