1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
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.
8  *
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.
13  *
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.
17  *
18  * File:    class.c
19  *
20  * Purpose: This file contains implementation of the "class" code.
21  */
22 
23 /** \file objects/UML/class.c  Implementation of the 'UML - Class' type */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <assert.h>
30 #include <gtk/gtk.h>
31 #include <math.h>
32 #include <string.h>
33 
34 #include "intl.h"
35 #include "diarenderer.h"
36 #include "attributes.h"
37 #include "properties.h"
38 #include "diamenu.h"
39 
40 #include "class.h"
41 
42 #include "pixmaps/umlclass.xpm"
43 
44 #include "debug.h"
45 
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
50 
51 static real umlclass_distance_from(UMLClass *umlclass, Point *point);
52 static void umlclass_select(UMLClass *umlclass, Point *clicked_point,
53 			    DiaRenderer *interactive_renderer);
54 static ObjectChange* umlclass_move_handle(UMLClass *umlclass, Handle *handle,
55 				 Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers);
56 static ObjectChange* umlclass_move(UMLClass *umlclass, Point *to);
57 static void umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer);
58 static DiaObject *umlclass_create(Point *startpoint,
59 			       void *user_data,
60 			       Handle **handle1,
61 			       Handle **handle2);
62 static void umlclass_destroy(UMLClass *umlclass);
63 static DiaObject *umlclass_copy(UMLClass *umlclass);
64 
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);
69 
70 static DiaMenu * umlclass_object_menu(DiaObject *obj, Point *p);
71 static ObjectChange *umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data);
72 
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);
76 
77 static void fill_in_fontdata(UMLClass *umlclass);
78 static int umlclass_num_dynamic_connectionpoints(UMLClass *class);
79 
80 static ObjectChange *_umlclass_apply_props_from_dialog(UMLClass *umlclass, GtkWidget *widget);
81 
82 static ObjectTypeOps umlclass_type_ops =
83 {
84   (CreateFunc) umlclass_create,
85   (LoadFunc)   umlclass_load,
86   (SaveFunc)   umlclass_save
87 };
88 
89 /**
90  * This is the type descriptor for a UML - Class. It contains the
91  * information used by Dia to create an object of this type. The structure
92  * of this data type is defined in the header file object.h. When a
93  * derivation of class is required, then this type can be copied and then
94  * change the name and any other fields that are variances from the base
95  * type.
96 */
97 DiaObjectType umlclass_type =
98 {
99   "UML - Class",   /* name */
100   0,                      /* version */
101   (char **) umlclass_xpm,  /* pixmap */
102 
103   &umlclass_type_ops,       /* ops */
104   NULL,
105   (void*)0
106 };
107 
108 /** \brief vtable for UMLClass */
109 static ObjectOps umlclass_ops = {
110   (DestroyFunc)         umlclass_destroy,
111   (DrawFunc)            umlclass_draw,
112   (DistanceFunc)        umlclass_distance_from,
113   (SelectFunc)          umlclass_select,
114   (CopyFunc)            umlclass_copy,
115   (MoveFunc)            umlclass_move,
116   (MoveHandleFunc)      umlclass_move_handle,
117   (GetPropertiesFunc)   umlclass_get_properties,
118   (ApplyPropertiesDialogFunc) _umlclass_apply_props_from_dialog,
119   (ObjectMenuFunc)      umlclass_object_menu,
120   (DescribePropsFunc)   umlclass_describe_props,
121   (GetPropsFunc)        umlclass_get_props,
122   (SetPropsFunc)        umlclass_set_props,
123   (TextEditFunc) 0,
124   (ApplyPropertiesListFunc) object_apply_props,
125 };
126 
127 extern PropDescDArrayExtra umlattribute_extra;
128 extern PropDescDArrayExtra umloperation_extra;
129 extern PropDescDArrayExtra umlparameter_extra;
130 extern PropDescDArrayExtra umlformalparameter_extra;
131 
132 /** Properties of UMLClass */
133 static PropDescription umlclass_props[] = {
134   ELEMENT_COMMON_PROPERTIES,
135   PROP_STD_LINE_WIDTH_OPTIONAL,
136   /* 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 */
137   PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE|PROP_FLAG_STANDARD|PROP_FLAG_OPTIONAL),
138   PROP_STD_LINE_COLOUR_OPTIONAL,
139   PROP_STD_FILL_COLOUR_OPTIONAL,
140 
141   PROP_STD_NOTEBOOK_BEGIN,
142   PROP_NOTEBOOK_PAGE("class", PROP_FLAG_DONT_MERGE, N_("Class")),
143   { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_NO_DEFAULTS,
144   N_("Name"), NULL, NULL },
145   { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
146   N_("Stereotype"), NULL, NULL },
147   { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
148   N_("Comment"), NULL, NULL },
149   { "abstract", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
150   N_("Abstract"), NULL, NULL },
151   { "template", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_NO_DEFAULTS,
152   N_("Template"), NULL, NULL },
153 
154   { "suppress_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
155   N_("Suppress Attributes"), NULL, NULL },
156   { "suppress_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
157   N_("Suppress Operations"), NULL, NULL },
158   { "visible_attributes", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
159   N_("Visible Attributes"), NULL, NULL },
160   { "visible_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
161   N_("Visible Operations"), NULL, NULL },
162   { "visible_comments", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
163   N_("Visible Comments"), NULL, NULL },
164   { "wrap_operations", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
165   N_("Wrap Operations"), NULL, NULL },
166   { "wrap_after_char", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
167   N_("Wrap after char"), NULL, NULL },
168   { "comment_line_length", PROP_TYPE_INT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
169   N_("Comment line length"), NULL, NULL},
170   { "comment_tagging", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
171   N_("Comment tagging"), NULL, NULL},
172 
173   /* all this just to make the defaults selectable ... */
174   PROP_NOTEBOOK_PAGE("font", PROP_FLAG_DONT_MERGE, N_("Font")),
175   PROP_MULTICOL_BEGIN("class"),
176   PROP_MULTICOL_COLUMN("font"),
177   /* FIXME: apparently multicol does not work correctly, this should be FIRST column */
178   { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
179   N_("Normal"), NULL, NULL },
180   { "polymorphic_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
181   N_("Polymorphic"), NULL, NULL },
182   { "abstract_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
183   N_("Abstract"), NULL, NULL },
184   { "classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
185   N_("Classname"), NULL, NULL },
186   { "abstract_classname_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
187   N_("Abstract Classname"), NULL, NULL },
188   { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
189   N_("Comment"), NULL, NULL },
190 
191   PROP_MULTICOL_COLUMN("height"),
192   { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
193   N_(" "), NULL, NULL },
194   { "polymorphic_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
195   N_(" "), NULL, NULL },
196   { "abstract_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
197   N_(" "), NULL, NULL },
198   { "classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
199   N_(" "), NULL, NULL },
200   { "abstract_classname_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
201   N_(" "), NULL, NULL },
202   { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
203   N_(" "), NULL, NULL },
204   PROP_MULTICOL_END("class"),
205   PROP_STD_NOTEBOOK_END,
206 
207   /* these are used during load, but currently not during save */
208   { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
209   N_("Attributes"), NULL, NULL /* umlattribute_extra */ },
210   { "operations", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
211   N_("Operations"), NULL, NULL /* umloperations_extra */ },
212   /* the naming is questionable, but kept for compatibility */
213   { "templates", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
214   N_("Template Parameters"), NULL, NULL /* umlformalparameters_extra */ },
215 
216   PROP_DESC_END
217 };
218 
219 ObjectChange *
_umlclass_apply_props_from_dialog(UMLClass * umlclass,GtkWidget * widget)220 _umlclass_apply_props_from_dialog(UMLClass *umlclass, GtkWidget *widget)
221 {
222   DiaObject *obj = &umlclass->element.object;
223   /* fallback, if it isn't our dialog, e.g. during multiple selection change */
224   if (!umlclass->properties_dialog)
225     return object_apply_props_from_dialog (obj, widget);
226   else
227     return umlclass_apply_props_from_dialog (umlclass, widget);
228 }
229 
230 static PropDescription *
umlclass_describe_props(UMLClass * umlclass)231 umlclass_describe_props(UMLClass *umlclass)
232 {
233  if (umlclass_props[0].quark == 0) {
234     int i = 0;
235 
236     prop_desc_list_calculate_quarks(umlclass_props);
237     while (umlclass_props[i].name != NULL) {
238       /* can't do this static, at least not on win32
239        * due to relocation (initializer not a constant)
240        */
241       if (0 == strcmp(umlclass_props[i].name, "attributes"))
242         umlclass_props[i].extra_data = &umlattribute_extra;
243       else if (0 == strcmp(umlclass_props[i].name, "operations")) {
244         PropDescription *records = umloperation_extra.common.record;
245         int j = 0;
246 
247         umlclass_props[i].extra_data = &umloperation_extra;
248 	while (records[j].name != NULL) {
249           if (0 == strcmp(records[j].name, "parameters"))
250 	    records[j].extra_data = &umlparameter_extra;
251 	  j++;
252 	}
253       }
254       else if (0 == strcmp(umlclass_props[i].name, "templates"))
255         umlclass_props[i].extra_data = &umlformalparameter_extra;
256 
257       i++;
258     }
259   }
260   return umlclass_props;
261 }
262 
263 static PropOffset umlclass_offsets[] = {
264   ELEMENT_COMMON_PROPERTIES_OFFSETS,
265 
266   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(UMLClass, line_width) },
267   { "text_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, text_color) },
268   { "line_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, line_color) },
269   { "fill_colour", PROP_TYPE_COLOUR, offsetof(UMLClass, fill_color) },
270   { "name", PROP_TYPE_STRING, offsetof(UMLClass, name) },
271   { "stereotype", PROP_TYPE_STRING, offsetof(UMLClass, stereotype) },
272   { "comment", PROP_TYPE_STRING, offsetof(UMLClass, comment) },
273   { "abstract", PROP_TYPE_BOOL, offsetof(UMLClass, abstract) },
274   { "template", PROP_TYPE_BOOL, offsetof(UMLClass, template) },
275   { "suppress_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_attributes) },
276   { "visible_attributes", PROP_TYPE_BOOL, offsetof(UMLClass , visible_attributes) },
277   { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
278   { "suppress_operations", PROP_TYPE_BOOL, offsetof(UMLClass , suppress_operations) },
279   { "visible_operations", PROP_TYPE_BOOL, offsetof(UMLClass , visible_operations) },
280   { "visible_comments", PROP_TYPE_BOOL, offsetof(UMLClass , visible_comments) },
281   { "wrap_operations", PROP_TYPE_BOOL, offsetof(UMLClass , wrap_operations) },
282   { "wrap_after_char", PROP_TYPE_INT, offsetof(UMLClass , wrap_after_char) },
283   { "comment_line_length", PROP_TYPE_INT, offsetof(UMLClass, comment_line_length) },
284   { "comment_tagging", PROP_TYPE_BOOL, offsetof(UMLClass, comment_tagging) },
285 
286   /* all this just to make the defaults selectable ... */
287   PROP_OFFSET_MULTICOL_BEGIN("class"),
288   PROP_OFFSET_MULTICOL_COLUMN("font"),
289   { "normal_font", PROP_TYPE_FONT, offsetof(UMLClass, normal_font) },
290   { "abstract_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_font) },
291   { "polymorphic_font", PROP_TYPE_FONT, offsetof(UMLClass, polymorphic_font) },
292   { "classname_font", PROP_TYPE_FONT, offsetof(UMLClass, classname_font) },
293   { "abstract_classname_font", PROP_TYPE_FONT, offsetof(UMLClass, abstract_classname_font) },
294   { "comment_font", PROP_TYPE_FONT, offsetof(UMLClass, comment_font) },
295 
296   PROP_OFFSET_MULTICOL_COLUMN("height"),
297   { "normal_font_height", PROP_TYPE_REAL, offsetof(UMLClass, font_height) },
298   { "abstract_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_font_height) },
299   { "polymorphic_font_height", PROP_TYPE_REAL, offsetof(UMLClass, polymorphic_font_height) },
300   { "classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, classname_font_height) },
301   { "abstract_classname_font_height", PROP_TYPE_REAL, offsetof(UMLClass, abstract_classname_font_height) },
302   { "comment_font_height", PROP_TYPE_REAL, offsetof(UMLClass, comment_font_height) },
303   PROP_OFFSET_MULTICOL_END("class"),
304 
305   { "operations", PROP_TYPE_DARRAY, offsetof(UMLClass , operations) },
306   { "attributes", PROP_TYPE_DARRAY, offsetof(UMLClass , attributes) } ,
307   { "templates",  PROP_TYPE_DARRAY, offsetof(UMLClass , formal_params) } ,
308 
309   { NULL, 0, 0 },
310 };
311 
312 static void
umlclass_get_props(UMLClass * umlclass,GPtrArray * props)313 umlclass_get_props(UMLClass * umlclass, GPtrArray *props)
314 {
315   object_get_props_from_offsets(&umlclass->element.object,
316                                 umlclass_offsets, props);
317 }
318 
319 static DiaMenuItem umlclass_menu_items[] = {
320         { N_("Show Comments"), umlclass_show_comments_callback, NULL,
321           DIAMENU_ACTIVE|DIAMENU_TOGGLE },
322 };
323 
324 static DiaMenu umlclass_menu = {
325         N_("Class"),
326         sizeof(umlclass_menu_items)/sizeof(DiaMenuItem),
327         umlclass_menu_items,
328         NULL
329 };
330 
331 DiaMenu *
umlclass_object_menu(DiaObject * obj,Point * p)332 umlclass_object_menu(DiaObject *obj, Point *p)
333 {
334         umlclass_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
335                 (((UMLClass *)obj)->visible_comments?DIAMENU_TOGGLE_ON:0);
336 
337         return &umlclass_menu;
338 }
339 
340 typedef struct _CommentState {
341   ObjectState state;
342   gboolean    visible_comments;
343 } CommentState;
344 static ObjectState*
_comment_get_state(DiaObject * obj)345 _comment_get_state (DiaObject *obj)
346 {
347   CommentState *state = g_new (CommentState,1);
348   state->state.free = NULL; /* we don't have any pointers to free */
349   state->visible_comments = ((UMLClass *)obj)->visible_comments;
350   return (ObjectState *)state;
351 }
352 static void
_comment_set_state(DiaObject * obj,ObjectState * state)353 _comment_set_state (DiaObject *obj, ObjectState *state)
354 {
355   ((UMLClass *)obj)->visible_comments = ((CommentState *)state)->visible_comments;
356   g_free (state); /* rather strange convention set_state consumes the state */
357   umlclass_calculate_data((UMLClass *)obj);
358   umlclass_update_data((UMLClass *)obj);
359 }
360 
361 ObjectChange *
umlclass_show_comments_callback(DiaObject * obj,Point * pos,gpointer data)362 umlclass_show_comments_callback(DiaObject *obj, Point *pos, gpointer data)
363 {
364   ObjectState *old_state = _comment_get_state(obj);
365   ObjectChange *change = new_object_state_change(obj, old_state, _comment_get_state, _comment_set_state );
366 
367   ((UMLClass *)obj)->visible_comments = !((UMLClass *)obj)->visible_comments;
368   umlclass_calculate_data((UMLClass *)obj);
369   umlclass_update_data((UMLClass *)obj);
370   return change;
371 }
372 
373 static void
umlclass_set_props(UMLClass * umlclass,GPtrArray * props)374 umlclass_set_props(UMLClass *umlclass, GPtrArray *props)
375 {
376   /* now that operations/attributes can be set here as well we need to
377    * take for the number of connections update as well
378    * Note that due to a hack in umlclass_load, this is called before
379    * the normal connection points are set up.
380    */
381   DiaObject *obj = &umlclass->element.object;
382   GList *list;
383   int num;
384 
385   object_set_props_from_offsets(&umlclass->element.object, umlclass_offsets,
386                                 props);
387 
388   num = UMLCLASS_CONNECTIONPOINTS + umlclass_num_dynamic_connectionpoints(umlclass);
389 
390 #ifdef UML_MAINPOINT
391   obj->num_connections = num + 1;
392 #else
393   obj->num_connections = num;
394 #endif
395 
396   obj->connections =  g_realloc(obj->connections, obj->num_connections*sizeof(ConnectionPoint *));
397 
398   /* Update data: */
399   if (num > UMLCLASS_CONNECTIONPOINTS) {
400     int i;
401     /* this is just updating pointers to ConnectionPoint, the real connection handling is elsewhere.
402      * Note: Can't optimize here on number change cause the ops/attribs may have changed regardless of that.
403      */
404     i = UMLCLASS_CONNECTIONPOINTS;
405     list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
406     while (list != NULL) {
407       UMLAttribute *attr = (UMLAttribute *)list->data;
408 
409       uml_attribute_ensure_connection_points (attr, obj);
410       obj->connections[i] = attr->left_connection;
411       obj->connections[i]->object = obj;
412       i++;
413       obj->connections[i] = attr->right_connection;
414       obj->connections[i]->object = obj;
415       i++;
416       list = g_list_next(list);
417     }
418     list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
419     while (list != NULL) {
420       UMLOperation *op = (UMLOperation *)list->data;
421 
422       uml_operation_ensure_connection_points (op, obj);
423       obj->connections[i] = op->left_connection;
424       obj->connections[i]->object = obj;
425       i++;
426       obj->connections[i] = op->right_connection;
427       obj->connections[i]->object = obj;
428       i++;
429       list = g_list_next(list);
430     }
431   }
432 #ifdef UML_MAINPOINT
433   obj->connections[num] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
434   obj->connections[num]->object = obj;
435 #endif
436 
437   umlclass_calculate_data(umlclass);
438   umlclass_update_data(umlclass);
439 #ifdef DEBUG
440   /* Would like to sanity check here, but the call to object_load_props
441    * in umlclass_load means we will be called with inconsistent data. */
442   umlclass_sanity_check(umlclass, "After updating data");
443 #endif
444 }
445 
446 static real
umlclass_distance_from(UMLClass * umlclass,Point * point)447 umlclass_distance_from(UMLClass *umlclass, Point *point)
448 {
449   DiaObject *obj = &umlclass->element.object;
450   return distance_rectangle_point(&obj->bounding_box, point);
451 }
452 
453 static void
umlclass_select(UMLClass * umlclass,Point * clicked_point,DiaRenderer * interactive_renderer)454 umlclass_select(UMLClass *umlclass, Point *clicked_point,
455 	       DiaRenderer *interactive_renderer)
456 {
457   element_update_handles(&umlclass->element);
458 }
459 
460 static ObjectChange*
umlclass_move_handle(UMLClass * umlclass,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)461 umlclass_move_handle(UMLClass *umlclass, Handle *handle,
462 		     Point *to, ConnectionPoint *cp,
463                      HandleMoveReason reason, ModifierKeys modifiers)
464 {
465   assert(umlclass!=NULL);
466   assert(handle!=NULL);
467   assert(to!=NULL);
468 
469   assert(handle->id < UMLCLASS_CONNECTIONPOINTS);
470 
471   return NULL;
472 }
473 
474 static ObjectChange*
umlclass_move(UMLClass * umlclass,Point * to)475 umlclass_move(UMLClass *umlclass, Point *to)
476 {
477   umlclass->element.corner = *to;
478   umlclass_update_data(umlclass);
479 
480   return NULL;
481 }
482 /**
483  * underlines the text at the start point using the text to determine
484  * the length of the underline. Draw a line under the text represented by
485  * string using the selected renderer, color, and line width.  Since
486  * drawing this line will change the line width used by DIA, the current
487  * line width that DIA is using is also passed so it can be restored once
488  * the line has been drawn.
489  *
490  * @param  renderer     the renderer that will draw the line
491  * @param  StartPoint   the start of the line to be drawn
492  * @param  font         the font used to draw the text being underlined
493  * @param  font_height  the size in the y direction of the font used to draw the text
494  * @param  string       the text string that is to be underlined
495  * @param  color        the color of the line to draw
496  * @param  line_width   default line thickness
497  * @param  underline_width   the thickness of the line to draw
498  *
499  */
500 static void
uml_underline_text(DiaRenderer * renderer,Point StartPoint,DiaFont * font,real font_height,gchar * string,Color * color,real line_width,real underline_width)501 uml_underline_text(DiaRenderer  *renderer,
502                Point         StartPoint,
503                DiaFont      *font,
504                real          font_height,
505                gchar        *string,
506                Color        *color,
507                real          line_width,
508                real          underline_width)
509 {
510   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
511   Point    UnderlineStartPoint;
512   Point    UnderlineEndPoint;
513   gchar *whitespaces;
514   int first_non_whitespace = 0;
515 
516   UnderlineStartPoint = StartPoint;
517   UnderlineStartPoint.y += font_height * 0.1;
518   UnderlineEndPoint = UnderlineStartPoint;
519 
520   whitespaces = string;
521   while (whitespaces &&
522 	 g_unichar_isspace(g_utf8_get_char(whitespaces))) {
523     whitespaces = g_utf8_next_char(whitespaces);
524   }
525   first_non_whitespace = whitespaces - string;
526   whitespaces = g_strdup(string);
527   whitespaces[first_non_whitespace] = '\0';
528   UnderlineStartPoint.x += dia_font_string_width(whitespaces, font, font_height);
529   g_free(whitespaces);
530   UnderlineEndPoint.x += dia_font_string_width(string, font, font_height);
531   renderer_ops->set_linewidth(renderer, underline_width);
532   renderer_ops->draw_line(renderer, &UnderlineStartPoint, &UnderlineEndPoint, color);
533   renderer_ops->set_linewidth(renderer, line_width);
534 }
535 
536 /**
537  * Create a documentation tag from a comment.
538  *
539  * First a string is created containing only the text
540  * "{documentation = ". Then the contents of the comment string
541  * are added but wrapped. This is done by first looking for any
542  * New Line characters. If the line segment is longer than the
543  * WrapPoint would allow, the line is broken at either the
544  * first whitespace before the WrapPoint or if there are no
545  * whitespaces in the segment, at the WrapPoint.  This
546  * continues until the entire string has been processed and
547  * then the resulting new string is returned. No attempt is
548  * made to rejoin any of the segments, that is all New Lines
549  * are treated as hard newlines. No syllable matching is done
550  * either so breaks in words will sometimes not make real
551  * sense.
552  * <p>
553  * Finally, since this function returns newly created dynamic
554  * memory the caller must free the memory to prevent memory
555  * leaks.
556  *
557  * @param  comment       The comment to be wrapped to the line length limit
558  * @param  WrapPoint     The maximum line length allowed for the line.
559  * @param  NumberOfLines The number of comment lines after the wrapping.
560  * @return               a pointer to the wrapped documentation
561  *
562  *  NOTE:
563  *      This function should most likely be move to a source file for
564  *      handling global UML functionallity at some point.
565  */
566 static gchar *
uml_create_documentation_tag(gchar * comment,gboolean tagging,gint WrapPoint,gint * NumberOfLines)567 uml_create_documentation_tag (gchar * comment,
568                               gboolean tagging,
569 			      gint WrapPoint,
570 			      gint *NumberOfLines)
571 {
572   gchar  *CommentTag           = tagging ? "{documentation = " : "";
573   gint   TagLength             = strlen(CommentTag);
574   /* Make sure that there is at least some value greater then zero for the WrapPoint to
575    * support diagrams from earlier versions of Dia. So if the WrapPoint is zero then use
576    * the taglength as the WrapPoint. If the Tag has been changed such that it has a length
577    * of 0 then use 1.
578    */
579   gint     WorkingWrapPoint = (TagLength<WrapPoint) ? WrapPoint : ((TagLength<=0)?1:TagLength);
580   gint     RawLength        = TagLength + strlen(comment) + (tagging?1:0);
581   gint     MaxCookedLength  = RawLength + RawLength/WorkingWrapPoint;
582   gchar    *WrappedComment  = g_malloc0(MaxCookedLength+1);
583   gint     AvailSpace       = WorkingWrapPoint - TagLength;
584   gchar    *Scan;
585   gchar    *BreakCandidate;
586   gunichar ScanChar;
587   gboolean AddNL            = FALSE;
588 
589   if (tagging)
590     strcat(WrappedComment, CommentTag);
591   *NumberOfLines = 1;
592 
593   while ( *comment ) {
594     /* Skip spaces */
595     while ( *comment && g_unichar_isspace(g_utf8_get_char(comment)) ) {
596         comment = g_utf8_next_char(comment);
597     }
598     /* Copy chars */
599     if ( *comment ){
600       /* Scan to \n or avalable space exhausted */
601       Scan = comment;
602       BreakCandidate = NULL;
603       while ( *Scan && *Scan != '\n' && AvailSpace > 0 ) {
604         ScanChar = g_utf8_get_char(Scan);
605         /* We known, that g_unichar_isspace() is not recommended for word breaking;
606          * but Pango usage seems too complex.
607          */
608         if ( g_unichar_isspace(ScanChar) )
609           BreakCandidate = Scan;
610         AvailSpace--; /* not valid for nonspacing marks */
611         Scan = g_utf8_next_char(Scan);
612       }
613       if ( AvailSpace==0 && BreakCandidate != NULL )
614         Scan = BreakCandidate;
615       if ( AddNL ){
616         strcat(WrappedComment, "\n");
617         *NumberOfLines+=1;
618       }
619       AddNL = TRUE;
620       strncat(WrappedComment, comment, Scan-comment);
621         AvailSpace = WorkingWrapPoint;
622       comment = Scan;
623     }
624   }
625   if (tagging)
626     strcat(WrappedComment, "}");
627   assert(strlen(WrappedComment)<=MaxCookedLength);
628   return WrappedComment;
629 }
630 
631 /**
632  * Draw the comment at the point, p, using the comment font from the
633  * class defined by umlclass. When complete update the point to reflect
634  * the size of data drawn.
635  * The comment will have been word wrapped using the function
636  * uml_create_documentation_tag, so it may have more than one line on the
637  * display.
638  *
639  * @param   renderer            The Renderer on which the comment is being drawn
640  * @param   font                The font to render the comment in.
641  * @param   font_height         The Y size of the font used to render the comment
642  * @param   text_color          A pointer to the color to use to render the comment
643  * @param   comment             The comment string to render
644  * @param   comment_tagging     If the {documentation = } tag should be enforced
645  * @param   Comment_line_length The maximum length of any one line in the comment
646  * @param   p                   The point at which the comment is to start
647  * @param   alignment           The method to use for alignment of the font
648  * @see   uml_create_documentation
649  */
650 static void
uml_draw_comments(DiaRenderer * renderer,DiaFont * font,real font_height,Color * text_color,gchar * comment,gboolean comment_tagging,gint Comment_line_length,Point * p,gint alignment)651 uml_draw_comments(DiaRenderer *renderer,
652                   DiaFont     *font,
653                   real         font_height,
654                   Color       *text_color,
655                   gchar       *comment,
656 		  gboolean     comment_tagging,
657                   gint         Comment_line_length,
658                   Point       *p,
659                   gint         alignment)
660 {
661   gint      NumberOfLines = 0;
662   gint      Index;
663   real      ascent;
664   gchar     *CommentString = 0;
665   gchar     *NewLineP= NULL;
666   gchar     *RenderP;
667 
668   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
669 
670   CommentString =
671         uml_create_documentation_tag(comment, comment_tagging, Comment_line_length, &NumberOfLines);
672   RenderP = CommentString;
673   renderer_ops->set_font(renderer, font, font_height);
674   ascent = dia_font_ascent(RenderP, font, font_height);
675   for ( Index=0; Index < NumberOfLines; Index++)
676   {
677     NewLineP = strchr(RenderP, '\n');
678     if ( NewLineP != NULL)
679     {
680       *NewLineP++ = '\0';
681     }
682     if (Index == 0) {
683       p->y += ascent;
684     }
685     else
686     {
687       p->y += font_height;                    /* Advance to the next line */
688     }
689     renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
690     RenderP = NewLineP;
691     if ( NewLineP == NULL){
692         break;
693     }
694   }
695   p->y += font_height - ascent;
696   g_free(CommentString);
697 }
698 
699 
700 /**
701  * Draw the name box of the class icon. According to the UML specification,
702  * the Name box or compartment is the top most compartment of the class
703  * icon. It may contain one or more stereotype declarations, followed by
704  * the name of the class. The name may be rendered to indicate abstraction
705  * for abstract classes. Following the name is any tagged values such as
706  * the {documentation = } tag.
707  * <p>
708  * Because the start point is the upper left of the class box, templates
709  * tend to get lost when created. By applying an offset, they will not be
710  * lost. The offset should only be added if the elem->corner.y = 0.
711  *
712  * @param umlclass  The pointer to the class being drawn
713  * @param renderer  The pointer to the rendering object used to draw
714  * @param elem      The pointer to the element within the class to be drawn
715  * @param offset    offset from start point
716  * @return  The offset from the start of the class to the bottom of the namebox
717  *
718  */
719 static real
umlclass_draw_namebox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem)720 umlclass_draw_namebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem )
721 {
722   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
723   real     font_height;
724   real     ascent;
725   DiaFont *font;
726   Point   StartPoint;
727   Point   LowerRightPoint;
728   real    Yoffset;
729   Color   *text_color = &umlclass->text_color;
730 
731 
732 
733   StartPoint.x = elem->corner.x;
734   StartPoint.y = elem->corner.y;
735 
736   Yoffset = elem->corner.y + umlclass->namebox_height;
737 
738   LowerRightPoint = StartPoint;
739   LowerRightPoint.x += elem->width;
740   LowerRightPoint.y  = Yoffset;
741 
742   /*
743    * First draw the outer box and fill color for the class name
744    * object
745    */
746   renderer_ops->fill_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->fill_color);
747   renderer_ops->draw_rect(renderer, &StartPoint, &LowerRightPoint, &umlclass->line_color);
748 
749   /* Start at the midpoint on the X axis */
750   StartPoint.x += elem->width / 2.0;
751   StartPoint.y += 0.2;
752 
753   /* stereotype: */
754   if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
755     gchar *String = umlclass->stereotype_string;
756     ascent = dia_font_ascent(String, umlclass->normal_font, umlclass->font_height);
757     StartPoint.y += ascent;
758     renderer_ops->set_font(renderer, umlclass->normal_font, umlclass->font_height);
759     renderer_ops->draw_string(renderer,  String, &StartPoint, ALIGN_CENTER, text_color);
760     StartPoint.y += umlclass->font_height - ascent;
761   }
762 
763   /* name: */
764   if (umlclass->name != NULL) {
765     if (umlclass->abstract) {
766       font = umlclass->abstract_classname_font;
767       font_height = umlclass->abstract_classname_font_height;
768     } else {
769       font = umlclass->classname_font;
770       font_height = umlclass->classname_font_height;
771     }
772     ascent = dia_font_ascent(umlclass->name, font, font_height);
773     StartPoint.y += ascent;
774 
775     renderer_ops->set_font(renderer, font, font_height);
776     renderer_ops->draw_string(renderer, umlclass->name, &StartPoint, ALIGN_CENTER, text_color);
777     StartPoint.y += font_height - ascent;
778   }
779 
780   /* comment */
781   if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0'){
782     uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
783                            &umlclass->text_color, umlclass->comment, umlclass->comment_tagging,
784                            umlclass->comment_line_length, &StartPoint, ALIGN_CENTER);
785   }
786   return Yoffset;
787 }
788 
789 /**
790  * Draw the attribute box.
791  * This attribute box follows the name box in the class icon. If the
792  * attributes are not suppress, draw each of the attributes following the
793  * UML specification for displaying attributes. Each attribute is preceded
794  * by the visibility character, +, - or # depending on whether it is public
795  * private or protected. If the attribute is "abstract" it will be rendered
796  * using the abstract font otherwise it will be rendered using the normal
797  * font. If the attribute is of class scope, static in C++, then it will be
798  * underlined. If there is a comment associated with the attribute, that is
799  * within the class description, it will be rendered as a uml comment.
800  *
801  * @param umlclass   The pointer to the class being drawn
802  * @param renderer   The pointer to the rendering object used to draw
803  * @param elem       The pointer to the element within the class to be drawn
804  * @param Yoffset    The Y offset from the start of the class at which to draw the attributebox
805  * @return           The offset from the start of the class to the bottom of the attributebox
806  * @see uml_draw_comments
807  */
808 static real
umlclass_draw_attributebox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem,real Yoffset)809 umlclass_draw_attributebox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
810 {
811   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
812   real     font_height;
813   real     ascent;
814   Point    StartPoint;
815   Point    LowerRight;
816   DiaFont *font;
817   Color   *fill_color = &umlclass->fill_color;
818   Color   *line_color = &umlclass->line_color;
819   Color   *text_color = &umlclass->text_color;
820   GList   *list;
821 
822   StartPoint.x = elem->corner.x;
823   StartPoint.y = Yoffset;
824   Yoffset   += umlclass->attributesbox_height;
825 
826   LowerRight   = StartPoint;
827   LowerRight.x += elem->width;
828   LowerRight.y = Yoffset;
829 
830   renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
831   renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
832 
833   if (!umlclass->suppress_attributes) {
834     gint i = 0;
835     StartPoint.x += (umlclass->line_width/2.0 + 0.1);
836     StartPoint.y += 0.1;
837 
838     list = umlclass->attributes;
839     while (list != NULL)
840     {
841       UMLAttribute *attr   = (UMLAttribute *)list->data;
842       gchar        *attstr = uml_get_attribute_string(attr);
843 
844       if (attr->abstract)  {
845         font = umlclass->abstract_font;
846         font_height = umlclass->abstract_font_height;
847       }
848       else  {
849         font = umlclass->normal_font;
850         font_height = umlclass->font_height;
851       }
852       ascent = dia_font_ascent(attstr, font, font_height);
853       StartPoint.y += ascent;
854       renderer_ops->set_font (renderer, font, font_height);
855       renderer_ops->draw_string(renderer, attstr, &StartPoint, ALIGN_LEFT, text_color);
856       StartPoint.y += font_height - ascent;
857 
858       if (attr->class_scope) {
859         uml_underline_text(renderer, StartPoint, font, font_height, attstr, line_color,
860                         umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
861       }
862 
863       if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
864         uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
865                                &umlclass->text_color, attr->comment, umlclass->comment_tagging,
866                                umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
867         StartPoint.y += umlclass->comment_font_height/2;
868       }
869       list = g_list_next(list);
870       i++;
871       g_free (attstr);
872     }
873   }
874   return Yoffset;
875 }
876 
877 
878 /**
879  * Draw the operations box. The operations block follows the attribute box
880  * if it is visible. If the operations are not suppressed, they are
881  * displayed in the operations box. Like the attributes, operations have
882  * visibility characters, +,-, and # indicating whether the are public,
883  * private or protected. The operations are rendered in different fonts
884  * depending on whether they are abstract (pure virtual), polymorphic
885  * (virtual) or leaf (final virtual or non-virtual). The parameters to the
886  * operation may be displayed and if they are they may be conditionally
887  * wrapped to reduce horizontial size of the icon.
888  *
889  * @param umlclass  The pointer to the class being drawn
890  * @param renderer  The pointer to the rendering object used to draw
891  * @param elem      The pointer to the element within the class to be drawn
892  * @param Yoffset   The Y offset from the start of the class at which to draw the operationbox
893  * @return          The offset from the start of the class to the bottom of the operationbox
894  *
895  */
896 static real
umlclass_draw_operationbox(UMLClass * umlclass,DiaRenderer * renderer,Element * elem,real Yoffset)897 umlclass_draw_operationbox(UMLClass *umlclass, DiaRenderer *renderer, Element *elem, real Yoffset)
898 {
899   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
900   real     font_height;
901   Point    StartPoint;
902   Point    LowerRight;
903   DiaFont *font;
904   GList   *list;
905   Color   *fill_color = &umlclass->fill_color;
906   Color   *line_color = &umlclass->line_color;
907   Color   *text_color = &umlclass->text_color;
908 
909 
910   StartPoint.x = elem->corner.x;
911   StartPoint.y = Yoffset;
912   Yoffset   += umlclass->operationsbox_height;
913 
914   LowerRight   = StartPoint;
915   LowerRight.x += elem->width;
916   LowerRight.y = Yoffset;
917 
918   renderer_ops->fill_rect(renderer, &StartPoint, &LowerRight, fill_color);
919   renderer_ops->draw_rect(renderer, &StartPoint, &LowerRight, line_color);
920 
921   if (!umlclass->suppress_operations) {
922     gint i = 0;
923     GList *wrapsublist = NULL;
924     gchar *part_opstr = NULL;
925     int wrap_pos, last_wrap_pos, ident, wrapping_needed;
926     int part_opstr_len = 0, part_opstr_need = 0;
927 
928     StartPoint.x += (umlclass->line_width/2.0 + 0.1);
929     StartPoint.y += 0.1;
930 
931     list = umlclass->operations;
932     while (list != NULL) {
933       UMLOperation *op = (UMLOperation *)list->data;
934       gchar* opstr = uml_get_operation_string(op);
935       real ascent;
936 
937       switch (op->inheritance_type) {
938       case UML_ABSTRACT:
939         font = umlclass->abstract_font;
940         font_height = umlclass->abstract_font_height;
941         break;
942       case UML_POLYMORPHIC:
943         font = umlclass->polymorphic_font;
944         font_height = umlclass->polymorphic_font_height;
945         break;
946       case UML_LEAF:
947       default:
948         font = umlclass->normal_font;
949         font_height = umlclass->font_height;
950       }
951 
952       wrapping_needed = 0;
953       if( umlclass->wrap_operations ) {
954 	wrapsublist = op->wrappos;
955       }
956 
957       ascent = dia_font_ascent(opstr, font, font_height);
958       op->ascent = ascent;
959       renderer_ops->set_font(renderer, font, font_height);
960 
961       if( umlclass->wrap_operations && op->needs_wrapping) {
962 	ident = op->wrap_indent;
963 	wrapsublist = op->wrappos;
964         wrap_pos = last_wrap_pos = 0;
965 
966         while( wrapsublist != NULL)   {
967           wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
968 
969           if( last_wrap_pos == 0)  {
970             part_opstr_need = wrap_pos + 1;
971             if (part_opstr_len < part_opstr_need) {
972               part_opstr_len = part_opstr_need;
973               part_opstr = g_realloc (part_opstr, part_opstr_need);
974             }
975             strncpy( part_opstr, opstr, wrap_pos);
976             memset( part_opstr+wrap_pos, '\0', 1);
977           }
978           else   {
979             part_opstr_need = ident + wrap_pos - last_wrap_pos + 1;
980             if (part_opstr_len < part_opstr_need) {
981               part_opstr_len = part_opstr_need;
982               part_opstr = g_realloc (part_opstr, part_opstr_need);
983             }
984             memset( part_opstr, ' ', ident);
985             memset( part_opstr+ident, '\0', 1);
986             strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
987           }
988 
989           if( last_wrap_pos == 0 ) {
990             StartPoint.y += ascent;
991           }
992           else
993           {
994             StartPoint.y += font_height;
995           }
996           renderer_ops->draw_string(renderer, part_opstr, &StartPoint, ALIGN_LEFT, text_color);
997 	  if (op->class_scope) {
998 	    uml_underline_text(renderer, StartPoint, font, font_height, part_opstr, line_color,
999 			       umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
1000 	  }
1001           last_wrap_pos = wrap_pos;
1002           wrapsublist = g_list_next( wrapsublist);
1003         }
1004       }
1005       else
1006       {
1007         StartPoint.y += ascent;
1008         renderer_ops->draw_string(renderer, opstr, &StartPoint, ALIGN_LEFT, text_color);
1009 	if (op->class_scope) {
1010 	  uml_underline_text(renderer, StartPoint, font, font_height, opstr, line_color,
1011 			     umlclass->line_width, UMLCLASS_UNDERLINEWIDTH );
1012 	}
1013       }
1014 
1015 
1016       StartPoint.y += font_height - ascent;
1017 
1018       if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1019         uml_draw_comments(renderer, umlclass->comment_font ,umlclass->comment_font_height,
1020                                &umlclass->text_color, op->comment, umlclass->comment_tagging,
1021                                umlclass->comment_line_length, &StartPoint, ALIGN_LEFT);
1022         StartPoint.y += umlclass->comment_font_height/2;
1023       }
1024 
1025       list = g_list_next(list);
1026       i++;
1027       g_free (opstr);
1028     }
1029     if (part_opstr){
1030       g_free(part_opstr);
1031     }
1032   }
1033   return Yoffset;
1034 }
1035 
1036 /**
1037  * Draw the template parameters box in the upper right hand corner of the
1038  * class box for paramertize classes (aka template classes). Fill in this
1039  * box with the parameters for the class.
1040  * <p>
1041  * At this time there is no provision for adding comments or documentation
1042  * to the display.
1043  *
1044  * @param umlclass  The pointer to the class being drawn
1045  * @param renderer  The pointer to the rendering object used to draw
1046  * @param elem      The pointer to the element within the class to be drawn
1047  *
1048  */
1049 static void
umlclass_draw_template_parameters_box(UMLClass * umlclass,DiaRenderer * renderer,Element * elem)1050 umlclass_draw_template_parameters_box(UMLClass *umlclass, DiaRenderer *renderer, Element *elem)
1051 {
1052   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1053   Point UpperLeft;
1054   Point LowerRight;
1055   Point TextInsert;
1056   GList *list;
1057   gint   i;
1058   DiaFont   *font = umlclass->normal_font;
1059   real       font_height = umlclass->font_height;
1060   real       ascent;
1061   Color     *fill_color = &umlclass->fill_color;
1062   Color     *line_color = &umlclass->line_color;
1063   Color     *text_color = &umlclass->text_color;
1064 
1065 
1066   /*
1067    * Adjust for the overlay of the template on the class icon
1068    */
1069   UpperLeft.x = elem->corner.x + elem->width - UMLCLASS_TEMPLATE_OVERLAY_X;
1070   UpperLeft.y =  elem->corner.y - umlclass->templates_height + UMLCLASS_TEMPLATE_OVERLAY_Y;
1071   TextInsert = UpperLeft;
1072   LowerRight = UpperLeft;
1073   LowerRight.x += umlclass->templates_width;
1074   LowerRight.y += umlclass->templates_height;
1075 
1076   renderer_ops->fill_rect(renderer, &UpperLeft, &LowerRight, fill_color);
1077   renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
1078   renderer_ops->set_dashlength(renderer, 0.3);
1079   renderer_ops->draw_rect(renderer, &UpperLeft, &LowerRight, line_color);
1080 
1081   TextInsert.x += 0.3;
1082   TextInsert.y += 0.1;
1083   renderer_ops->set_font(renderer, font, font_height);
1084   i = 0;
1085   list = umlclass->formal_params;
1086   while (list != NULL)
1087   {
1088     gchar *paramstr = uml_get_formalparameter_string((UMLFormalParameter *)list->data);
1089 
1090     ascent = dia_font_ascent(paramstr, font, font_height);
1091     TextInsert.y += ascent;
1092     renderer_ops->draw_string(renderer, paramstr, &TextInsert, ALIGN_LEFT, text_color);
1093     TextInsert.y += font_height - ascent;
1094 
1095     list = g_list_next(list);
1096     i++;
1097     g_free (paramstr);
1098   }
1099 }
1100 
1101 /**
1102  * Draw the class icon for the specified UMLClass object.
1103  * Set the renderer to the correct fill and line styles and the appropriate
1104  * line width.  The object is drawn by the umlclass_draw_namebox,
1105  * umlclass_draw_attributebox, umlclass_draw_operationbox and
1106  * umlclass_draw_template_parameters_box.
1107  *
1108  * @param  umlclass   object based on the uml class that is being rendered
1109  * @param   DiaRenderer  renderer used to draw the object
1110  *
1111  */
1112 
1113 static void
umlclass_draw(UMLClass * umlclass,DiaRenderer * renderer)1114 umlclass_draw(UMLClass *umlclass, DiaRenderer *renderer)
1115 {
1116   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1117   real     y  = 0.0;
1118   Element *elem;
1119 
1120   assert(umlclass != NULL);
1121   assert(renderer != NULL);
1122 
1123   renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
1124   renderer_ops->set_linewidth(renderer, umlclass->line_width);
1125   renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
1126 
1127   elem = &umlclass->element;
1128 
1129   y = umlclass_draw_namebox(umlclass, renderer, elem);
1130   if (umlclass->visible_attributes) {
1131     y = umlclass_draw_attributebox(umlclass, renderer, elem, y);
1132   }
1133   if (umlclass->visible_operations) {
1134     y = umlclass_draw_operationbox(umlclass, renderer, elem, y);
1135   }
1136   if (umlclass->template) {
1137     umlclass_draw_template_parameters_box(umlclass, renderer, elem);
1138   }
1139 }
1140 
1141 void
umlclass_update_data(UMLClass * umlclass)1142 umlclass_update_data(UMLClass *umlclass)
1143 {
1144   Element *elem = &umlclass->element;
1145   DiaObject *obj = &elem->object;
1146   real x,y;
1147   GList *list;
1148   int i;
1149   int pointswide;
1150   int lowerleftcorner;
1151   real pointspacing;
1152 
1153   x = elem->corner.x;
1154   y = elem->corner.y;
1155 
1156   /* Update connections: */
1157   umlclass->connections[0].pos = elem->corner;
1158   umlclass->connections[0].directions = DIR_NORTH|DIR_WEST;
1159 
1160   /* there are four corner points and two side points, thus all
1161    * remaining points are on the top/bottom width
1162    */
1163   pointswide = (UMLCLASS_CONNECTIONPOINTS - 6) / 2;
1164   pointspacing = elem->width / (pointswide + 1.0);
1165 
1166   /* across the top connection points */
1167   for (i=1;i<=pointswide;i++) {
1168     umlclass->connections[i].pos.x = x + (pointspacing * i);
1169     umlclass->connections[i].pos.y = y;
1170     umlclass->connections[i].directions = DIR_NORTH;
1171   }
1172 
1173   i = (UMLCLASS_CONNECTIONPOINTS / 2) - 2;
1174   umlclass->connections[i].pos.x = x + elem->width;
1175   umlclass->connections[i].pos.y = y;
1176   umlclass->connections[i].directions = DIR_NORTH|DIR_EAST;
1177 
1178   i = (UMLCLASS_CONNECTIONPOINTS / 2) - 1;
1179   umlclass->connections[i].pos.x = x;
1180   umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1181   umlclass->connections[i].directions = DIR_WEST;
1182 
1183   i = (UMLCLASS_CONNECTIONPOINTS / 2);
1184   umlclass->connections[i].pos.x = x + elem->width;
1185   umlclass->connections[i].pos.y = y + umlclass->namebox_height / 2.0;
1186   umlclass->connections[i].directions = DIR_EAST;
1187 
1188   i = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1189   umlclass->connections[i].pos.x = x;
1190   umlclass->connections[i].pos.y = y + elem->height;
1191   umlclass->connections[i].directions = DIR_WEST|DIR_SOUTH;
1192 
1193   /* across the bottom connection points */
1194   lowerleftcorner = (UMLCLASS_CONNECTIONPOINTS / 2) + 1;
1195   for (i=1;i<=pointswide;i++) {
1196     umlclass->connections[lowerleftcorner + i].pos.x = x + (pointspacing * i);
1197     umlclass->connections[lowerleftcorner + i].pos.y = y + elem->height;
1198     umlclass->connections[lowerleftcorner + i].directions = DIR_SOUTH;
1199   }
1200 
1201   /* bottom-right corner */
1202   i = (UMLCLASS_CONNECTIONPOINTS) - 1;
1203   umlclass->connections[i].pos.x = x + elem->width;
1204   umlclass->connections[i].pos.y = y + elem->height;
1205   umlclass->connections[i].directions = DIR_EAST|DIR_SOUTH;
1206 
1207 #ifdef UML_MAINPOINT
1208   /* Main point -- lives just after fixed connpoints in umlclass array */
1209   i = UMLCLASS_CONNECTIONPOINTS;
1210   umlclass->connections[i].pos.x = x + elem->width / 2;
1211   umlclass->connections[i].pos.y = y + elem->height / 2;
1212   umlclass->connections[i].directions = DIR_ALL;
1213   umlclass->connections[i].flags = CP_FLAGS_MAIN;
1214 #endif
1215 
1216   y += umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1217 
1218   list = (!umlclass->visible_attributes || umlclass->suppress_attributes) ? NULL : umlclass->attributes;
1219   while (list != NULL) {
1220     UMLAttribute *attr = (UMLAttribute *)list->data;
1221 
1222     attr->left_connection->pos.x = x;
1223     attr->left_connection->pos.y = y;
1224     attr->left_connection->directions = DIR_WEST;
1225     attr->right_connection->pos.x = x + elem->width;
1226     attr->right_connection->pos.y = y;
1227     attr->right_connection->directions = DIR_EAST;
1228 
1229     y += umlclass->font_height;
1230     if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0') {
1231       gint NumberOfLines = 0;
1232       gchar *CommentString = 0;
1233 
1234       CommentString =
1235         uml_create_documentation_tag(attr->comment, umlclass->comment_tagging, umlclass->comment_line_length, &NumberOfLines);
1236       g_free(CommentString);
1237       y += umlclass->comment_font_height*NumberOfLines + umlclass->comment_font_height/2;
1238     }
1239 
1240     list = g_list_next(list);
1241   }
1242 
1243   y = elem->corner.y + umlclass->namebox_height + 0.1 + umlclass->font_height/2;
1244   if (umlclass->visible_attributes) {
1245     y += umlclass->attributesbox_height;
1246   }
1247 
1248   list = (!umlclass->visible_operations || umlclass->suppress_operations) ? NULL : umlclass->operations;
1249   while (list != NULL) {
1250     UMLOperation *op = (UMLOperation *)list->data;
1251 
1252     op->left_connection->pos.x = x;
1253     op->left_connection->pos.y = y;
1254     op->left_connection->directions = DIR_WEST;
1255     op->right_connection->pos.x = x + elem->width;
1256     op->right_connection->pos.y = y;
1257     op->right_connection->directions = DIR_EAST;
1258 
1259     if (op->needs_wrapping) { /* Wrapped */
1260       int lines = g_list_length(op->wrappos);
1261       y += umlclass->font_height * lines;
1262     } else {
1263       y += umlclass->font_height;
1264     }
1265     if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0') {
1266       gint NumberOfLines = 0;
1267       gchar *CommentString = 0;
1268 
1269       CommentString =
1270         uml_create_documentation_tag(op->comment, umlclass->comment_tagging, umlclass->comment_line_length, &NumberOfLines);
1271       g_free(CommentString);
1272       y += umlclass->comment_font_height*NumberOfLines + umlclass->comment_font_height/2;
1273     }
1274     list = g_list_next(list);
1275   }
1276 
1277   element_update_boundingbox(elem);
1278 
1279   if (umlclass->template) {
1280     /* fix boundingumlclass for templates: */
1281     obj->bounding_box.top -= (umlclass->templates_height  - UMLCLASS_TEMPLATE_OVERLAY_Y) ;
1282     obj->bounding_box.right += (umlclass->templates_width - UMLCLASS_TEMPLATE_OVERLAY_X);
1283     obj->bounding_box.left  -= (elem->width < UMLCLASS_TEMPLATE_OVERLAY_X) ?
1284 				(UMLCLASS_TEMPLATE_OVERLAY_X - elem->width) : 0;
1285   }
1286 
1287   obj->position = elem->corner;
1288 
1289   element_update_handles(elem);
1290 
1291 #ifdef DEBUG
1292   umlclass_sanity_check(umlclass, "After updating data");
1293 #endif
1294 }
1295 
1296 /**
1297  * Calculate the dimensions of the class icons namebox for a given object of UMLClass.
1298  * The height is stored in the class structure. When calculating the
1299  * comment, if any, the comment is word wrapped and the resulting number of
1300  * lines is then used to calculate the height of the bounding box.
1301  *
1302  * @param   umlclass  pointer to the object of UMLClass to calculate
1303  * @return            the horizontal size of the name box.
1304  *
1305  */
1306 
1307 static real
umlclass_calculate_name_data(UMLClass * umlclass)1308 umlclass_calculate_name_data(UMLClass *umlclass)
1309 {
1310   real   maxwidth = 0.0;
1311   real   width = 0.0;
1312   /* name box: */
1313 
1314   if (umlclass->name != NULL && umlclass->name[0] != '\0') {
1315     if (umlclass->abstract) {
1316       maxwidth = dia_font_string_width(umlclass->name,
1317                                        umlclass->abstract_classname_font,
1318                                        umlclass->abstract_classname_font_height);
1319     } else {
1320       maxwidth = dia_font_string_width(umlclass->name,
1321                                        umlclass->classname_font,
1322                                        umlclass->classname_font_height);
1323     }
1324   }
1325 
1326   umlclass->namebox_height = umlclass->classname_font_height + 4*0.1;
1327   if (umlclass->stereotype_string != NULL) {
1328     g_free(umlclass->stereotype_string);
1329   }
1330   if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0') {
1331     umlclass->namebox_height += umlclass->font_height;
1332     umlclass->stereotype_string = g_strconcat ( UML_STEREOTYPE_START,
1333 			                                    umlclass->stereotype,
1334 			                                    UML_STEREOTYPE_END,
1335 			                                    NULL);
1336 
1337     width = dia_font_string_width (umlclass->stereotype_string,
1338                                    umlclass->normal_font,
1339                                    umlclass->font_height);
1340     maxwidth = MAX(width, maxwidth);
1341   } else {
1342     umlclass->stereotype_string = NULL;
1343   }
1344 
1345   if (umlclass->visible_comments && umlclass->comment != NULL && umlclass->comment[0] != '\0')
1346   {
1347     int NumberOfLines = 0;
1348     gchar *CommentString = uml_create_documentation_tag (umlclass->comment,
1349                                                          umlclass->comment_tagging,
1350                                                          umlclass->comment_line_length,
1351                                                          &NumberOfLines);
1352     width = dia_font_string_width (CommentString,
1353                                     umlclass->comment_font,
1354                                     umlclass->comment_font_height);
1355 
1356     g_free(CommentString);
1357     umlclass->namebox_height += umlclass->comment_font_height * NumberOfLines;
1358     maxwidth = MAX(width, maxwidth);
1359   }
1360   return maxwidth;
1361 }
1362 
1363 /**
1364  * Calculate the dimensions of the attribute box on an object of type UMLClass.
1365  * @param   umlclass  a pointer to an object of UMLClass
1366  * @return            the horizontal size of the attribute box
1367  *
1368  */
1369 
1370 static real
umlclass_calculate_attribute_data(UMLClass * umlclass)1371 umlclass_calculate_attribute_data(UMLClass *umlclass)
1372 {
1373   int    i;
1374   real   maxwidth = 0.0;
1375   real   width    = 0.0;
1376   GList *list;
1377 
1378   umlclass->attributesbox_height = 2*0.1;
1379 
1380   if (g_list_length(umlclass->attributes) != 0)
1381   {
1382     i = 0;
1383     list = umlclass->attributes;
1384     while (list != NULL)
1385     {
1386       UMLAttribute *attr   = (UMLAttribute *) list->data;
1387       gchar        *attstr = uml_get_attribute_string(attr);
1388 
1389       if (attr->abstract)
1390       {
1391         width = dia_font_string_width(attstr,
1392                                       umlclass->abstract_font,
1393                                       umlclass->abstract_font_height);
1394         umlclass->attributesbox_height += umlclass->abstract_font_height;
1395       }
1396       else
1397       {
1398         width = dia_font_string_width(attstr,
1399                                       umlclass->normal_font,
1400                                       umlclass->font_height);
1401         umlclass->attributesbox_height += umlclass->font_height;
1402       }
1403       maxwidth = MAX(width, maxwidth);
1404 
1405       if (umlclass->visible_comments && attr->comment != NULL && attr->comment[0] != '\0')
1406       {
1407         int NumberOfLines = 0;
1408         gchar *CommentString = uml_create_documentation_tag(attr->comment,
1409                                                             umlclass->comment_tagging,
1410                                                             umlclass->comment_line_length,
1411                                                             &NumberOfLines);
1412         width = dia_font_string_width(CommentString,
1413                                        umlclass->comment_font,
1414                                        umlclass->comment_font_height);
1415         g_free(CommentString);
1416         umlclass->attributesbox_height += umlclass->comment_font_height * NumberOfLines + umlclass->comment_font_height/2;
1417         maxwidth = MAX(width, maxwidth);
1418       }
1419 
1420       i++;
1421       list = g_list_next(list);
1422       g_free (attstr);
1423     }
1424   }
1425 
1426   if ((umlclass->attributesbox_height<0.4)|| umlclass->suppress_attributes )
1427   {
1428     umlclass->attributesbox_height = 0.4;
1429   }
1430   return maxwidth;
1431 }
1432 
1433 /**
1434  * Calculate the dimensions of the operations box of an object of  UMLClass.
1435  * The vertical size or height is stored in the object.
1436  * @param   umlclass  a pointer to an object of UMLClass
1437  * @return         the horizontial size of the operations box
1438  *
1439  */
1440 
1441 static real
umlclass_calculate_operation_data(UMLClass * umlclass)1442 umlclass_calculate_operation_data(UMLClass *umlclass)
1443 {
1444   int    i;
1445   int    pos_next_comma;
1446   int    pos_brace;
1447   int    wrap_pos;
1448   int    last_wrap_pos;
1449   int    indent;
1450   int    offset;
1451   int    maxlinewidth;
1452   int    length;
1453   real   maxwidth = 0.0;
1454   real   width    = 0.0;
1455   GList *list;
1456   GList *wrapsublist;
1457 
1458   /* operations box: */
1459   umlclass->operationsbox_height = 2*0.1;
1460 
1461   if (0 != g_list_length(umlclass->operations))
1462   {
1463     i = 0;
1464     list = umlclass->operations;
1465     while (list != NULL)
1466     {
1467       UMLOperation *op = (UMLOperation *) list->data;
1468       gchar *opstr = uml_get_operation_string(op);
1469       DiaFont   *Font;
1470       real       FontHeight;
1471 
1472       length = strlen( (const gchar*)opstr);
1473 
1474       if (op->wrappos != NULL) {
1475 	g_list_free(op->wrappos);
1476       }
1477       op->wrappos = NULL;
1478 
1479       switch(op->inheritance_type)
1480       {
1481 	  case UML_ABSTRACT:
1482 	    Font       =  umlclass->abstract_font;
1483 	    FontHeight =  umlclass->abstract_font_height;
1484 	    break;
1485 	  case UML_POLYMORPHIC:
1486 	    Font       =  umlclass->polymorphic_font;
1487 	    FontHeight =  umlclass->polymorphic_font_height;
1488 	    break;
1489 	  case UML_LEAF:
1490 	  default:
1491 	    Font       = umlclass->normal_font;
1492 	    FontHeight = umlclass->font_height;
1493       }
1494       op->ascent = dia_font_ascent(opstr, Font, FontHeight);
1495 
1496       if( umlclass->wrap_operations )
1497       {
1498         if( length > umlclass->wrap_after_char)
1499         {
1500           gchar *part_opstr;
1501 	  op->needs_wrapping = TRUE;
1502 
1503           /* count maximal line width to create a secure buffer (part_opstr)
1504           and build the sublist with the wrapping data for the current operation, which will be used by umlclass_draw(), too.
1505 	  */
1506           pos_next_comma = pos_brace = wrap_pos = offset
1507 	    = maxlinewidth = umlclass->max_wrapped_line_width = 0;
1508           while( wrap_pos + offset < length)
1509           {
1510             do
1511             {
1512               pos_next_comma = strcspn( (const gchar*)opstr + wrap_pos + offset, ",");
1513               wrap_pos += pos_next_comma + 1;
1514             } while( wrap_pos < umlclass->wrap_after_char - pos_brace
1515 		     && wrap_pos + offset < length);
1516 
1517             if( offset == 0){
1518               pos_brace = strcspn( opstr, "(");
1519 	      op->wrap_indent = pos_brace + 1;
1520             }
1521 	    op->wrappos = g_list_append(op->wrappos,
1522 					GINT_TO_POINTER(wrap_pos + offset));
1523 
1524             maxlinewidth = MAX(maxlinewidth, wrap_pos);
1525 
1526             offset += wrap_pos;
1527             wrap_pos = 0;
1528           }
1529           umlclass->max_wrapped_line_width = MAX( umlclass->max_wrapped_line_width, maxlinewidth+1);
1530 
1531 	  indent = op->wrap_indent;
1532           part_opstr = g_alloca(umlclass->max_wrapped_line_width+indent+1);
1533 
1534 	  wrapsublist = op->wrappos;
1535           wrap_pos = last_wrap_pos = 0;
1536 
1537           while( wrapsublist != NULL){
1538             wrap_pos = GPOINTER_TO_INT( wrapsublist->data);
1539             if( last_wrap_pos == 0){
1540               strncpy( part_opstr, opstr, wrap_pos);
1541               memset( part_opstr+wrap_pos, '\0', 1);
1542             }
1543             else
1544             {
1545               memset( part_opstr, ' ', indent);
1546               memset( part_opstr+indent, '\0', 1);
1547               strncat( part_opstr, opstr+last_wrap_pos, wrap_pos-last_wrap_pos);
1548             }
1549 
1550             width = dia_font_string_width(part_opstr,Font,FontHeight);
1551             umlclass->operationsbox_height += FontHeight;
1552 
1553             maxwidth = MAX(width, maxwidth);
1554             last_wrap_pos = wrap_pos;
1555             wrapsublist = g_list_next( wrapsublist);
1556           }
1557         }
1558         else
1559         {
1560 	  op->needs_wrapping = FALSE;
1561         }
1562       }
1563 
1564       if (!(umlclass->wrap_operations && length > umlclass->wrap_after_char)) {
1565         switch(op->inheritance_type)
1566         {
1567         case UML_ABSTRACT:
1568           Font       =  umlclass->abstract_font;
1569           FontHeight =  umlclass->abstract_font_height;
1570           break;
1571         case UML_POLYMORPHIC:
1572           Font       =  umlclass->polymorphic_font;
1573           FontHeight =  umlclass->polymorphic_font_height;
1574           break;
1575         case UML_LEAF:
1576         default:
1577           Font       = umlclass->normal_font;
1578           FontHeight = umlclass->font_height;
1579         }
1580         width = dia_font_string_width(opstr,Font,FontHeight);
1581         umlclass->operationsbox_height += FontHeight;
1582 
1583         maxwidth = MAX(width, maxwidth);
1584       }
1585 
1586       if (umlclass->visible_comments && op->comment != NULL && op->comment[0] != '\0'){
1587         int NumberOfLines = 0;
1588         gchar *CommentString = uml_create_documentation_tag(op->comment,
1589                                                             umlclass->comment_tagging,
1590                                                             umlclass->comment_line_length,
1591                                                             &NumberOfLines);
1592         width = dia_font_string_width(CommentString,
1593                                        umlclass->comment_font,
1594                                        umlclass->comment_font_height);
1595         g_free(CommentString);
1596         umlclass->operationsbox_height += umlclass->comment_font_height * NumberOfLines + umlclass->comment_font_height/2;
1597         maxwidth = MAX(width, maxwidth);
1598       }
1599 
1600       i++;
1601       list = g_list_next(list);
1602       g_free (opstr);
1603     }
1604   }
1605 
1606   umlclass->element.width = maxwidth + 2*0.3;
1607 
1608   if ((umlclass->operationsbox_height<0.4) || umlclass->suppress_operations ) {
1609     umlclass->operationsbox_height = 0.4;
1610   }
1611 
1612   return maxwidth;
1613 }
1614 
1615 /**
1616  * calculate the size of the class icon for an object of UMLClass.
1617  * This is done by calculating the size of the text to be displayed within
1618  * each of the contained bounding boxes, name, attributes and operations.
1619  * Because the comments may require wrapping, each comment is wrapped and
1620  * the resulting number of lines is used to calculate the size of the
1621  * comment within the box. The various font settings with in the class
1622  * properties contribute to the overall size of the resulting bounding box.
1623  *
1624  *  * @param   umlclass  a pointer to an object of UMLClass
1625  *
1626  */
1627 void
umlclass_calculate_data(UMLClass * umlclass)1628 umlclass_calculate_data(UMLClass *umlclass)
1629 {
1630   int    i;
1631   int    num_templates;
1632   real   maxwidth = 0.0;
1633   real   width;
1634   GList *list;
1635 
1636   if (!umlclass->destroyed)
1637   {
1638     maxwidth = MAX(umlclass_calculate_name_data(umlclass),      maxwidth);
1639 
1640     umlclass->element.height = umlclass->namebox_height;
1641 
1642     if (umlclass->visible_attributes){
1643       maxwidth = MAX(umlclass_calculate_attribute_data(umlclass), maxwidth);
1644       umlclass->element.height += umlclass->attributesbox_height;
1645     }
1646     if (umlclass->visible_operations){
1647       maxwidth = MAX(umlclass_calculate_operation_data(umlclass), maxwidth);
1648       umlclass->element.height += umlclass->operationsbox_height;
1649     }
1650     umlclass->element.width  = maxwidth+0.5;
1651     /* templates box: */
1652     num_templates = g_list_length(umlclass->formal_params);
1653 
1654     umlclass->templates_height =
1655       umlclass->font_height * num_templates + 2*0.1;
1656     umlclass->templates_height = MAX(umlclass->templates_height, 0.4);
1657 
1658 
1659     maxwidth = UMLCLASS_TEMPLATE_OVERLAY_X;
1660     if (num_templates != 0)
1661     {
1662       i = 0;
1663       list = umlclass->formal_params;
1664       while (list != NULL)
1665       {
1666         UMLFormalParameter *param = (UMLFormalParameter *) list->data;
1667 	gchar *paramstr = uml_get_formalparameter_string(param);
1668 
1669         width = dia_font_string_width(paramstr,
1670                                       umlclass->normal_font,
1671                                       umlclass->font_height);
1672         maxwidth = MAX(width, maxwidth);
1673 
1674         i++;
1675         list = g_list_next(list);
1676 	g_free (paramstr);
1677       }
1678     }
1679     umlclass->templates_width = maxwidth + 2*0.2;
1680   }
1681 }
1682 
1683 static void
fill_in_fontdata(UMLClass * umlclass)1684 fill_in_fontdata(UMLClass *umlclass)
1685 {
1686    if (umlclass->normal_font == NULL) {
1687      umlclass->font_height = 0.8;
1688      umlclass->normal_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, 0.8);
1689    }
1690    if (umlclass->abstract_font == NULL) {
1691      umlclass->abstract_font_height = 0.8;
1692      umlclass->abstract_font =
1693        dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD, 0.8);
1694    }
1695    if (umlclass->polymorphic_font == NULL) {
1696      umlclass->polymorphic_font_height = 0.8;
1697      umlclass->polymorphic_font =
1698        dia_font_new_from_style(DIA_FONT_MONOSPACE | DIA_FONT_ITALIC, 0.8);
1699    }
1700    if (umlclass->classname_font == NULL) {
1701      umlclass->classname_font_height = 1.0;
1702      umlclass->classname_font =
1703        dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD, 1.0);
1704    }
1705    if (umlclass->abstract_classname_font == NULL) {
1706      umlclass->abstract_classname_font_height = 1.0;
1707      umlclass->abstract_classname_font =
1708        dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC, 1.0);
1709    }
1710    if (umlclass->comment_font == NULL) {
1711      umlclass->comment_font_height = 0.7;
1712      umlclass->comment_font = dia_font_new_from_style(DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1713    }
1714 }
1715 /**
1716  * Create an object of type class
1717  * By default this will create a object of class UMLClass. Howerver there
1718  * are at least two types of UMLClass objects, so the user_data is selects
1719  * the correct UMLClass object. Other than that this is quite straight
1720  * forward. The key to the polymorphic nature of this object is the use of
1721  * the DiaObjectType record which in conjunction with the user_data
1722  * controls the specific derived object type.
1723  *
1724  * @param  startpoint   the origin of the object being created
1725  * @param  user_data	Information used by this routine to create the appropriate object
1726  * @param  handle1		ignored when creating a class object
1727  * @param  handle2      ignored when creating a class object
1728  * @return               a pointer to the object created
1729  *
1730  *  NOTE:
1731  *      This function should most likely be move to a source file for
1732  *      handling global UML functionallity at some point.
1733  */
1734 
1735 static DiaObject *
umlclass_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)1736 umlclass_create(Point *startpoint,
1737 	       void *user_data,
1738   	       Handle **handle1,
1739 	       Handle **handle2)
1740 {
1741   UMLClass *umlclass;
1742   Element *elem;
1743   DiaObject *obj;
1744   int i;
1745 
1746   umlclass = g_malloc0(sizeof(UMLClass));
1747   elem = &umlclass->element;
1748   obj = &elem->object;
1749 
1750 
1751   elem->corner = *startpoint;
1752 
1753 #ifdef UML_MAINPOINT
1754   element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1); /* No attribs or ops => 0 extra connectionpoints. */
1755 #else
1756   element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS); /* No attribs or ops => 0 extra connectionpoints. */
1757 #endif
1758 
1759   umlclass->properties_dialog = NULL;
1760   fill_in_fontdata(umlclass);
1761 
1762 
1763   /*
1764    * The following block of code may need to be converted to a switch statement if more than
1765    * two types of objects can be made - Dave Klotzbach
1766    */
1767   umlclass->template = (GPOINTER_TO_INT(user_data)==1);
1768 
1769   if (umlclass->template){
1770     umlclass->name = g_strdup (_("Template"));
1771   }
1772   else {
1773     umlclass->name = g_strdup (_("Class"));
1774   }
1775   obj->type = &umlclass_type;
1776   obj->ops = &umlclass_ops;
1777 
1778   umlclass->stereotype = NULL;
1779   umlclass->comment = NULL;
1780 
1781   umlclass->abstract = FALSE;
1782 
1783   umlclass->suppress_attributes = FALSE;
1784   umlclass->suppress_operations = FALSE;
1785 
1786   umlclass->visible_attributes = TRUE;
1787   umlclass->visible_operations = TRUE;
1788   umlclass->visible_comments = FALSE;
1789 
1790   umlclass->wrap_operations = TRUE;
1791   umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
1792 
1793   umlclass->attributes = NULL;
1794 
1795   umlclass->operations = NULL;
1796 
1797   umlclass->formal_params = NULL;
1798 
1799   umlclass->stereotype_string = NULL;
1800 
1801   umlclass->line_width = attributes_get_default_linewidth();
1802   umlclass->text_color = color_black;
1803   umlclass->line_color = attributes_get_foreground();
1804   umlclass->fill_color = attributes_get_background();
1805 
1806   umlclass_calculate_data(umlclass);
1807 
1808   for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
1809     obj->connections[i] = &umlclass->connections[i];
1810     umlclass->connections[i].object = obj;
1811     umlclass->connections[i].connected = NULL;
1812   }
1813 #ifdef UML_MAINPOINT
1814   /* Put mainpoint at the end, after conditional attr/oprn points,
1815    * but store it in the local connectionpoint array. */
1816   i += umlclass_num_dynamic_connectionpoints(umlclass);
1817   obj->connections[i] = &umlclass->connections[UMLCLASS_CONNECTIONPOINTS];
1818   umlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = obj;
1819   umlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
1820 #endif
1821 
1822   elem->extra_spacing.border_trans = umlclass->line_width/2.0;
1823   umlclass_update_data(umlclass);
1824 
1825   for (i=0;i<8;i++) {
1826     obj->handles[i]->type = HANDLE_NON_MOVABLE;
1827   }
1828 
1829   *handle1 = NULL;
1830   *handle2 = NULL;
1831   return &umlclass->element.object;
1832 }
1833 
1834 static void
umlclass_destroy(UMLClass * umlclass)1835 umlclass_destroy(UMLClass *umlclass)
1836 {
1837   GList *list;
1838   UMLAttribute *attr;
1839   UMLOperation *op;
1840   UMLFormalParameter *param;
1841 
1842 #ifdef DEBUG
1843   umlclass_sanity_check(umlclass, "Destroying");
1844 #endif
1845 
1846   umlclass->destroyed = TRUE;
1847 
1848   dia_font_unref(umlclass->normal_font);
1849   dia_font_unref(umlclass->abstract_font);
1850   dia_font_unref(umlclass->polymorphic_font);
1851   dia_font_unref(umlclass->classname_font);
1852   dia_font_unref(umlclass->abstract_classname_font);
1853   dia_font_unref(umlclass->comment_font);
1854 
1855   element_destroy(&umlclass->element);
1856 
1857   g_free(umlclass->name);
1858   g_free(umlclass->stereotype);
1859   g_free(umlclass->comment);
1860 
1861   list = umlclass->attributes;
1862   while (list != NULL) {
1863     attr = (UMLAttribute *)list->data;
1864     g_free(attr->left_connection);
1865     g_free(attr->right_connection);
1866     uml_attribute_destroy(attr);
1867     list = g_list_next(list);
1868   }
1869   g_list_free(umlclass->attributes);
1870 
1871   list = umlclass->operations;
1872   while (list != NULL) {
1873     op = (UMLOperation *)list->data;
1874     g_free(op->left_connection);
1875     g_free(op->right_connection);
1876     uml_operation_destroy(op);
1877     list = g_list_next(list);
1878   }
1879   g_list_free(umlclass->operations);
1880 
1881   list = umlclass->formal_params;
1882   while (list != NULL) {
1883     param = (UMLFormalParameter *)list->data;
1884     uml_formalparameter_destroy(param);
1885     list = g_list_next(list);
1886   }
1887   g_list_free(umlclass->formal_params);
1888 
1889   if (umlclass->stereotype_string != NULL) {
1890     g_free(umlclass->stereotype_string);
1891   }
1892 
1893   if (umlclass->properties_dialog != NULL) {
1894     umlclass_dialog_free (umlclass->properties_dialog);
1895   }
1896 }
1897 
1898 static DiaObject *
umlclass_copy(UMLClass * umlclass)1899 umlclass_copy(UMLClass *umlclass)
1900 {
1901   int i;
1902   UMLClass *newumlclass;
1903   Element *elem, *newelem;
1904   DiaObject *newobj;
1905   GList *list;
1906   UMLFormalParameter *param;
1907 
1908   elem = &umlclass->element;
1909 
1910   newumlclass = g_malloc0(sizeof(UMLClass));
1911   newelem = &newumlclass->element;
1912   newobj = &newelem->object;
1913 
1914   element_copy(elem, newelem);
1915 
1916   newumlclass->font_height = umlclass->font_height;
1917   newumlclass->abstract_font_height = umlclass->abstract_font_height;
1918   newumlclass->polymorphic_font_height = umlclass->polymorphic_font_height;
1919   newumlclass->classname_font_height = umlclass->classname_font_height;
1920   newumlclass->abstract_classname_font_height =
1921           umlclass->abstract_classname_font_height;
1922   newumlclass->comment_font_height =
1923           umlclass->comment_font_height;
1924 
1925   newumlclass->normal_font =
1926           dia_font_copy(umlclass->normal_font);
1927   newumlclass->abstract_font =
1928           dia_font_copy(umlclass->abstract_font);
1929   newumlclass->polymorphic_font =
1930           dia_font_copy(umlclass->polymorphic_font);
1931   newumlclass->classname_font =
1932           dia_font_copy(umlclass->classname_font);
1933   newumlclass->abstract_classname_font =
1934           dia_font_copy(umlclass->abstract_classname_font);
1935   newumlclass->comment_font =
1936           dia_font_copy(umlclass->comment_font);
1937 
1938   newumlclass->name = g_strdup(umlclass->name);
1939   if (umlclass->stereotype != NULL && umlclass->stereotype[0] != '\0')
1940     newumlclass->stereotype = g_strdup(umlclass->stereotype);
1941   else
1942     newumlclass->stereotype = NULL;
1943 
1944   if (umlclass->comment != NULL)
1945     newumlclass->comment = g_strdup(umlclass->comment);
1946   else
1947     newumlclass->comment = NULL;
1948 
1949   newumlclass->abstract = umlclass->abstract;
1950   newumlclass->suppress_attributes = umlclass->suppress_attributes;
1951   newumlclass->suppress_operations = umlclass->suppress_operations;
1952   newumlclass->visible_attributes = umlclass->visible_attributes;
1953   newumlclass->visible_operations = umlclass->visible_operations;
1954   newumlclass->visible_comments = umlclass->visible_comments;
1955   newumlclass->wrap_operations = umlclass->wrap_operations;
1956   newumlclass->wrap_after_char = umlclass->wrap_after_char;
1957   newumlclass->comment_line_length = umlclass->comment_line_length;
1958   newumlclass->comment_tagging = umlclass->comment_tagging;
1959   newumlclass->line_width = umlclass->line_width;
1960   newumlclass->text_color = umlclass->text_color;
1961   newumlclass->line_color = umlclass->line_color;
1962   newumlclass->fill_color = umlclass->fill_color;
1963 
1964   newumlclass->attributes = NULL;
1965   list = umlclass->attributes;
1966   while (list != NULL) {
1967     UMLAttribute *attr = (UMLAttribute *)list->data;
1968     /* not copying the connection, if there was one */
1969     UMLAttribute *newattr = uml_attribute_copy(attr);
1970     uml_attribute_ensure_connection_points (newattr, newobj);
1971 
1972     newumlclass->attributes = g_list_append(newumlclass->attributes,
1973 					    newattr);
1974     list = g_list_next(list);
1975   }
1976 
1977   newumlclass->operations = NULL;
1978   list = umlclass->operations;
1979   while (list != NULL) {
1980     UMLOperation *op = (UMLOperation *)list->data;
1981     UMLOperation *newop = uml_operation_copy(op);
1982     uml_operation_ensure_connection_points (newop, newobj);
1983 
1984     newumlclass->operations = g_list_append(newumlclass->operations,
1985 					     newop);
1986     list = g_list_next(list);
1987   }
1988 
1989   newumlclass->template = umlclass->template;
1990 
1991   newumlclass->formal_params = NULL;
1992   list = umlclass->formal_params;
1993   while (list != NULL) {
1994     param = (UMLFormalParameter *)list->data;
1995     newumlclass->formal_params =
1996       g_list_append(newumlclass->formal_params,
1997 		     uml_formalparameter_copy(param));
1998     list = g_list_next(list);
1999   }
2000 
2001   newumlclass->properties_dialog = NULL;
2002 
2003   newumlclass->stereotype_string = NULL;
2004 
2005   for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2006     newobj->connections[i] = &newumlclass->connections[i];
2007     newumlclass->connections[i].object = newobj;
2008     newumlclass->connections[i].connected = NULL;
2009     newumlclass->connections[i].pos = umlclass->connections[i].pos;
2010     newumlclass->connections[i].last_pos = umlclass->connections[i].last_pos;
2011   }
2012 
2013   umlclass_calculate_data(newumlclass);
2014 
2015   i = UMLCLASS_CONNECTIONPOINTS;
2016   if ( (newumlclass->visible_attributes) &&
2017        (!newumlclass->suppress_attributes)) {
2018     list = newumlclass->attributes;
2019     while (list != NULL) {
2020       UMLAttribute *attr = (UMLAttribute *)list->data;
2021       newobj->connections[i++] = attr->left_connection;
2022       newobj->connections[i++] = attr->right_connection;
2023 
2024       list = g_list_next(list);
2025     }
2026   }
2027 
2028   if ( (newumlclass->visible_operations) &&
2029        (!newumlclass->suppress_operations)) {
2030     list = newumlclass->operations;
2031     while (list != NULL) {
2032       UMLOperation *op = (UMLOperation *)list->data;
2033       newobj->connections[i++] = op->left_connection;
2034       newobj->connections[i++] = op->right_connection;
2035 
2036       list = g_list_next(list);
2037     }
2038   }
2039 
2040 #ifdef UML_MAINPOINT
2041   newobj->connections[i] = &newumlclass->connections[UMLCLASS_CONNECTIONPOINTS];
2042   newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].object = newobj;
2043   newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].connected = NULL;
2044   newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos =
2045     umlclass->connections[UMLCLASS_CONNECTIONPOINTS].pos;
2046   newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos =
2047     umlclass->connections[UMLCLASS_CONNECTIONPOINTS].last_pos;
2048   newumlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags =
2049     umlclass->connections[UMLCLASS_CONNECTIONPOINTS].flags;
2050   i++;
2051 #endif
2052 
2053   umlclass_update_data(newumlclass);
2054 
2055 #ifdef DEBUG
2056   umlclass_sanity_check(newumlclass, "Copied");
2057 #endif
2058 
2059   return &newumlclass->element.object;
2060 }
2061 
2062 
2063 static void
umlclass_save(UMLClass * umlclass,ObjectNode obj_node,const char * filename)2064 umlclass_save(UMLClass *umlclass, ObjectNode obj_node,
2065 	      const char *filename)
2066 {
2067   UMLAttribute *attr;
2068   UMLOperation *op;
2069   UMLFormalParameter *formal_param;
2070   GList *list;
2071   AttributeNode attr_node;
2072 
2073 #ifdef DEBUG
2074   umlclass_sanity_check(umlclass, "Saving");
2075 #endif
2076 
2077   element_save(&umlclass->element, obj_node);
2078 
2079   /* Class info: */
2080   data_add_string(new_attribute(obj_node, "name"),
2081 		  umlclass->name);
2082   data_add_string(new_attribute(obj_node, "stereotype"),
2083 		  umlclass->stereotype);
2084   data_add_string(new_attribute(obj_node, "comment"),
2085                   umlclass->comment);
2086   data_add_boolean(new_attribute(obj_node, "abstract"),
2087 		   umlclass->abstract);
2088   data_add_boolean(new_attribute(obj_node, "suppress_attributes"),
2089 		   umlclass->suppress_attributes);
2090   data_add_boolean(new_attribute(obj_node, "suppress_operations"),
2091 		   umlclass->suppress_operations);
2092   data_add_boolean(new_attribute(obj_node, "visible_attributes"),
2093 		   umlclass->visible_attributes);
2094   data_add_boolean(new_attribute(obj_node, "visible_operations"),
2095 		   umlclass->visible_operations);
2096   data_add_boolean(new_attribute(obj_node, "visible_comments"),
2097 		   umlclass->visible_comments);
2098   data_add_boolean(new_attribute(obj_node, "wrap_operations"),
2099 		   umlclass->wrap_operations);
2100   data_add_int(new_attribute(obj_node, "wrap_after_char"),
2101 		   umlclass->wrap_after_char);
2102   data_add_int(new_attribute(obj_node, "comment_line_length"),
2103                    umlclass->comment_line_length);
2104   data_add_boolean(new_attribute(obj_node, "comment_tagging"),
2105                    umlclass->comment_tagging);
2106   data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
2107 		   umlclass->line_width);
2108   data_add_color(new_attribute(obj_node, "line_color"),
2109 		   &umlclass->line_color);
2110   data_add_color(new_attribute(obj_node, "fill_color"),
2111 		   &umlclass->fill_color);
2112   data_add_color(new_attribute(obj_node, "text_color"),
2113 		   &umlclass->text_color);
2114   data_add_font (new_attribute (obj_node, "normal_font"),
2115                  umlclass->normal_font);
2116   data_add_font (new_attribute (obj_node, "abstract_font"),
2117                  umlclass->abstract_font);
2118   data_add_font (new_attribute (obj_node, "polymorphic_font"),
2119                  umlclass->polymorphic_font);
2120   data_add_font (new_attribute (obj_node, "classname_font"),
2121                  umlclass->classname_font);
2122   data_add_font (new_attribute (obj_node, "abstract_classname_font"),
2123                  umlclass->abstract_classname_font);
2124   data_add_font (new_attribute (obj_node, "comment_font"),
2125                  umlclass->comment_font);
2126   data_add_real (new_attribute (obj_node, "normal_font_height"),
2127                  umlclass->font_height);
2128   data_add_real (new_attribute (obj_node, "polymorphic_font_height"),
2129                  umlclass->polymorphic_font_height);
2130   data_add_real (new_attribute (obj_node, "abstract_font_height"),
2131                  umlclass->abstract_font_height);
2132   data_add_real (new_attribute (obj_node, "classname_font_height"),
2133                  umlclass->classname_font_height);
2134   data_add_real (new_attribute (obj_node, "abstract_classname_font_height"),
2135                  umlclass->abstract_classname_font_height);
2136   data_add_real (new_attribute (obj_node, "comment_font_height"),
2137                  umlclass->comment_font_height);
2138 
2139   /* Attribute info: */
2140   attr_node = new_attribute(obj_node, "attributes");
2141   list = umlclass->attributes;
2142   while (list != NULL) {
2143     attr = (UMLAttribute *) list->data;
2144     uml_attribute_write(attr_node, attr);
2145     list = g_list_next(list);
2146   }
2147 
2148   /* Operations info: */
2149   attr_node = new_attribute(obj_node, "operations");
2150   list = umlclass->operations;
2151   while (list != NULL) {
2152     op = (UMLOperation *) list->data;
2153     uml_operation_write(attr_node, op);
2154     list = g_list_next(list);
2155   }
2156 
2157   /* Template info: */
2158   data_add_boolean(new_attribute(obj_node, "template"),
2159 		   umlclass->template);
2160 
2161   attr_node = new_attribute(obj_node, "templates");
2162   list = umlclass->formal_params;
2163   while (list != NULL) {
2164     formal_param = (UMLFormalParameter *) list->data;
2165     uml_formalparameter_write(attr_node, formal_param);
2166     list = g_list_next(list);
2167   }
2168 }
2169 
umlclass_load(ObjectNode obj_node,int version,const char * filename)2170 static DiaObject *umlclass_load(ObjectNode obj_node, int version,
2171 			     const char *filename)
2172 {
2173   UMLClass *umlclass;
2174   Element *elem;
2175   DiaObject *obj;
2176   AttributeNode attr_node;
2177   int i;
2178   GList *list;
2179 
2180 
2181   umlclass = g_malloc0(sizeof(UMLClass));
2182   elem = &umlclass->element;
2183   obj = &elem->object;
2184 
2185   obj->type = &umlclass_type;
2186   obj->ops = &umlclass_ops;
2187 
2188   element_load(elem, obj_node);
2189 
2190 #ifdef UML_MAINPOINT
2191   element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS + 1);
2192 #else
2193   element_init(elem, 8, UMLCLASS_CONNECTIONPOINTS);
2194 #endif
2195 
2196   umlclass->properties_dialog = NULL;
2197 
2198   for (i=0;i<UMLCLASS_CONNECTIONPOINTS;i++) {
2199     obj->connections[i] = &umlclass->connections[i];
2200     umlclass->connections[i].object = obj;
2201     umlclass->connections[i].connected = NULL;
2202   }
2203 
2204   fill_in_fontdata(umlclass);
2205 
2206   /* kind of dirty, object_load_props() may leave us in an inconsistent state --hb */
2207   object_load_props(obj,obj_node);
2208 
2209   /* parameters loaded via StdProp dont belong here anymore. In case of strings they
2210    * will produce leaks. Otherwise the are just wasteing time (at runtime and while
2211    * reading the code). Except maybe for some compatibility stuff.
2212    * Although that *could* probably done via StdProp too.                      --hb
2213    */
2214 
2215   /* new since 0.94, don't wrap by default to keep old diagrams intact */
2216   umlclass->wrap_operations = FALSE;
2217   attr_node = object_find_attribute(obj_node, "wrap_operations");
2218   if (attr_node != NULL)
2219     umlclass->wrap_operations = data_boolean(attribute_first_data(attr_node));
2220 
2221   umlclass->wrap_after_char = UMLCLASS_WRAP_AFTER_CHAR;
2222   attr_node = object_find_attribute(obj_node, "wrap_after_char");
2223   if (attr_node != NULL)
2224     umlclass->wrap_after_char = data_int(attribute_first_data(attr_node));
2225 
2226   /* if it uses the new name the value is already set by object_load_props() above */
2227   umlclass->comment_line_length = UMLCLASS_COMMENT_LINE_LENGTH;
2228   attr_node = object_find_attribute(obj_node,"comment_line_length");
2229   /* support the unusal cased name, although it only existed in cvs version */
2230   if (attr_node == NULL)
2231     attr_node = object_find_attribute(obj_node,"Comment_line_length");
2232   if (attr_node != NULL)
2233     umlclass->comment_line_length = data_int(attribute_first_data(attr_node));
2234 
2235   /* compatibility with 0.94 and before as well as the temporary state with only 'comment_line_length' */
2236   umlclass->comment_tagging = (attr_node != NULL);
2237   attr_node = object_find_attribute(obj_node, "comment_tagging");
2238   if (attr_node != NULL)
2239     umlclass->comment_tagging = data_boolean(attribute_first_data(attr_node));
2240 
2241   /* Loads the line width */
2242   umlclass->line_width = UMLCLASS_BORDER;
2243   attr_node = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
2244   if(attr_node != NULL)
2245     umlclass->line_width = data_real(attribute_first_data(attr_node));
2246 
2247   umlclass->line_color = color_black;
2248   /* support the old name ... */
2249   attr_node = object_find_attribute(obj_node, "foreground_color");
2250   if(attr_node != NULL)
2251     data_color(attribute_first_data(attr_node), &umlclass->line_color);
2252   umlclass->text_color = umlclass->line_color;
2253   /* ... but prefer the new one */
2254   attr_node = object_find_attribute(obj_node, "line_color");
2255   if(attr_node != NULL)
2256     data_color(attribute_first_data(attr_node), &umlclass->line_color);
2257   attr_node = object_find_attribute(obj_node, "text_color");
2258   if(attr_node != NULL)
2259     data_color(attribute_first_data(attr_node), &umlclass->text_color);
2260 
2261   umlclass->fill_color = color_white;
2262   /* support the old name ... */
2263   attr_node = object_find_attribute(obj_node, "background_color");
2264   if(attr_node != NULL)
2265     data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2266   /* ... but prefer the new one */
2267   attr_node = object_find_attribute(obj_node, "fill_color");
2268   if(attr_node != NULL)
2269     data_color(attribute_first_data(attr_node), &umlclass->fill_color);
2270 
2271   /* Attribute info: */
2272   list = umlclass->attributes;
2273   while (list) {
2274     UMLAttribute *attr = list->data;
2275     g_assert(attr);
2276 
2277     uml_attribute_ensure_connection_points (attr, obj);
2278     list = g_list_next(list);
2279   }
2280 
2281   /* Operations info: */
2282   list = umlclass->operations;
2283   while (list) {
2284     UMLOperation *op = (UMLOperation *)list->data;
2285     g_assert(op);
2286 
2287     uml_operation_ensure_connection_points (op, obj);
2288     list = g_list_next(list);
2289   }
2290 
2291   /* Template info: */
2292   umlclass->template = FALSE;
2293   attr_node = object_find_attribute(obj_node, "template");
2294   if (attr_node != NULL)
2295     umlclass->template = data_boolean(attribute_first_data(attr_node));
2296 
2297   fill_in_fontdata(umlclass);
2298 
2299   umlclass->stereotype_string = NULL;
2300 
2301   umlclass_calculate_data(umlclass);
2302 
2303   elem->extra_spacing.border_trans = umlclass->line_width/2.0;
2304   umlclass_update_data(umlclass);
2305 
2306   for (i=0;i<8;i++) {
2307     obj->handles[i]->type = HANDLE_NON_MOVABLE;
2308   }
2309 
2310 #ifdef DEBUG
2311   umlclass_sanity_check(umlclass, "Loaded class");
2312 #endif
2313 
2314   return &umlclass->element.object;
2315 }
2316 
2317 /** Returns the number of connection points used by the attributes and
2318  * connections in the current state of the object.
2319  */
2320 static int
umlclass_num_dynamic_connectionpoints(UMLClass * umlclass)2321 umlclass_num_dynamic_connectionpoints(UMLClass *umlclass) {
2322   int num = 0;
2323   if ( (umlclass->visible_attributes) &&
2324        (!umlclass->suppress_attributes)) {
2325     GList *list = umlclass->attributes;
2326     num += 2 * g_list_length(list);
2327   }
2328 
2329   if ( (umlclass->visible_operations) &&
2330        (!umlclass->suppress_operations)) {
2331     GList *list = umlclass->operations;
2332     num += 2 * g_list_length(list);
2333   }
2334   return num;
2335 }
2336 
2337 void
umlclass_sanity_check(UMLClass * c,gchar * msg)2338 umlclass_sanity_check(UMLClass *c, gchar *msg)
2339 {
2340 #ifdef UML_MAINPOINT
2341   int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS + 1;
2342 #else
2343   int num_fixed_connections = UMLCLASS_CONNECTIONPOINTS;
2344 #endif
2345   DiaObject *obj = (DiaObject*)c;
2346   GList *attrs;
2347   int i;
2348 
2349   dia_object_sanity_check((DiaObject *)c, msg);
2350 
2351   /* Check that num_connections is correct */
2352   dia_assert_true(num_fixed_connections + umlclass_num_dynamic_connectionpoints(c)
2353 		  == obj->num_connections,
2354 		  "%s: Class %p has %d connections, but %d fixed and %d dynamic\n",
2355 		  msg, c, obj->num_connections, num_fixed_connections,
2356 		  umlclass_num_dynamic_connectionpoints(c));
2357 
2358   for (i = 0; i < UMLCLASS_CONNECTIONPOINTS; i++) {
2359     dia_assert_true(&c->connections[i] == obj->connections[i],
2360 		    "%s: Class %p connection mismatch at %d: %p != %p\n",
2361 		    msg, c, i, &c->connections[i], obj->connections[i]);
2362   }
2363 
2364 #ifdef UML_MAINPOINT
2365   dia_assert_true(&c->connections[i] ==
2366 		  obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2367 		  "%s: Class %p mainpoint mismatch: %p != %p (at %d)\n",
2368 		  msg, c, i, &c->connections[i],
2369 		  obj->connections[i + umlclass_num_dynamic_connectionpoints(c)],
2370 		  i + umlclass_num_dynamic_connectionpoints(c));
2371 #endif
2372 
2373   /* Check that attributes are set up right. */
2374   i = 0;
2375   for (attrs = c->attributes; attrs != NULL; attrs = g_list_next(attrs)) {
2376     UMLAttribute *attr = (UMLAttribute *)attrs->data;
2377 
2378     dia_assert_true(attr->name != NULL,
2379 		    "%s: %p attr %d has null name\n",
2380 		    msg, c, i);
2381     dia_assert_true(attr->type != NULL,
2382 		    "%s: %p attr %d has null type\n",
2383 		    msg, c, i);
2384 #if 0 /* attr->comment == NULL is fine everywhere else */
2385     dia_assert_true(attr->comment != NULL,
2386 		    "%s: %p attr %d has null comment\n",
2387 		    msg, c, i);
2388 #endif
2389 
2390     /* the following checks are only right with visible attributes */
2391     if (c->visible_attributes && !c->suppress_attributes) {
2392       int conn_offset = UMLCLASS_CONNECTIONPOINTS + 2 * i;
2393 
2394       dia_assert_true(attr->left_connection != NULL,
2395 		      "%s: %p attr %d has null left connection\n",
2396 		      msg, c, i);
2397       dia_assert_true(attr->right_connection != NULL,
2398 		      "%s: %p attr %d has null right connection\n",
2399 		      msg, c, i);
2400 
2401       dia_assert_true(attr->left_connection == obj->connections[conn_offset],
2402 		      "%s: %p attr %d left conn %p doesn't match obj conn %d: %p\n",
2403 		      msg, c, i, attr->left_connection,
2404 		      conn_offset, obj->connections[conn_offset]);
2405       dia_assert_true(attr->right_connection == obj->connections[conn_offset + 1],
2406 		      "%s: %p attr %d right conn %p doesn't match obj conn %d: %p\n",
2407 		      msg, c, i, attr->right_connection,
2408 		      conn_offset + 1, obj->connections[conn_offset + 1]);
2409       i++;
2410     }
2411   }
2412   /* Check that operations are set up right. */
2413 }
2414 
2415 
2416