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