2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / objects / UML / component.c
blob4b1e4671a68a9b108b98b91f60df592ef5fd701f
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <assert.h>
24 #include <math.h>
25 #include <string.h>
27 #include "intl.h"
28 #include "object.h"
29 #include "element.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
32 #include "text.h"
33 #include "properties.h"
35 #include "stereotype.h"
37 #include "pixmaps/component.xpm"
39 #define NUM_CONNECTIONS 11
41 typedef struct _Component Component;
42 struct _Component {
43 Element element;
45 ConnectionPoint connections[NUM_CONNECTIONS];
47 char *stereotype;
48 Text *text;
50 char *st_stereotype;
51 TextAttributes attrs;
53 Color line_color;
54 Color fill_color;
57 #define COMPONENT_BORDERWIDTH 0.1
58 #define COMPONENT_CHEIGHT 0.7
59 #define COMPONENT_CWIDTH 2.0
60 #define COMPONENT_MARGIN_X 0.4
61 #define COMPONENT_MARGIN_Y 0.3
63 static real component_distance_from(Component *cmp, Point *point);
64 static void component_select(Component *cmp, Point *clicked_point,
65 DiaRenderer *interactive_renderer);
66 static ObjectChange* component_move_handle(Component *cmp, Handle *handle,
67 Point *to, ConnectionPoint *cp,
68 HandleMoveReason reason, ModifierKeys modifiers);
69 static ObjectChange* component_move(Component *cmp, Point *to);
70 static void component_draw(Component *cmp, DiaRenderer *renderer);
71 static DiaObject *component_create(Point *startpoint,
72 void *user_data,
73 Handle **handle1,
74 Handle **handle2);
75 static void component_destroy(Component *cmp);
76 static DiaObject *component_load(ObjectNode obj_node, int version,
77 const char *filename);
79 static PropDescription *component_describe_props(Component *component);
80 static void component_get_props(Component *component, GPtrArray *props);
81 static void component_set_props(Component *component, GPtrArray *props);
83 static void component_update_data(Component *cmp);
85 static ObjectTypeOps component_type_ops =
87 (CreateFunc) component_create,
88 (LoadFunc) component_load,/*using_properties*/ /* load */
89 (SaveFunc) object_save_using_properties, /* save */
90 (GetDefaultsFunc) NULL,
91 (ApplyDefaultsFunc) NULL
94 DiaObjectType component_type =
96 "UML - Component", /* name */
97 0, /* version */
98 (char **) component_xpm, /* pixmap */
100 &component_type_ops /* ops */
103 static ObjectOps component_ops = {
104 (DestroyFunc) component_destroy,
105 (DrawFunc) component_draw,
106 (DistanceFunc) component_distance_from,
107 (SelectFunc) component_select,
108 (CopyFunc) object_copy_using_properties,
109 (MoveFunc) component_move,
110 (MoveHandleFunc) component_move_handle,
111 (GetPropertiesFunc) object_create_props_dialog,
112 (ApplyPropertiesFunc) object_apply_props_from_dialog,
113 (ObjectMenuFunc) NULL,
114 (DescribePropsFunc) component_describe_props,
115 (GetPropsFunc) component_get_props,
116 (SetPropsFunc) component_set_props
119 static PropDescription component_props[] = {
120 ELEMENT_COMMON_PROPERTIES,
121 PROP_STD_LINE_COLOUR_OPTIONAL,
122 PROP_STD_FILL_COLOUR_OPTIONAL,
123 { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
124 N_("Stereotype"), NULL, NULL },
125 PROP_STD_TEXT_FONT,
126 PROP_STD_TEXT_HEIGHT,
127 PROP_STD_TEXT_COLOUR,
128 { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
129 PROP_DESC_END
132 static PropDescription *
133 component_describe_props(Component *component)
135 if (component_props[0].quark == 0) {
136 prop_desc_list_calculate_quarks(component_props);
138 return component_props;
141 static PropOffset component_offsets[] = {
142 ELEMENT_COMMON_PROPERTIES_OFFSETS,
143 {"line_colour",PROP_TYPE_COLOUR,offsetof(Component,line_color)},
144 {"fill_colour",PROP_TYPE_COLOUR,offsetof(Component,fill_color)},
145 {"stereotype", PROP_TYPE_STRING, offsetof(Component , stereotype) },
146 {"text",PROP_TYPE_TEXT,offsetof(Component,text)},
147 {"text_font",PROP_TYPE_FONT,offsetof(Component,attrs.font)},
148 {"text_height",PROP_TYPE_REAL,offsetof(Component,attrs.height)},
149 {"text_colour",PROP_TYPE_COLOUR,offsetof(Component,attrs.color)},
150 { NULL, 0, 0 },
154 static void
155 component_get_props(Component * component, GPtrArray *props)
157 text_get_attributes(component->text,&component->attrs);
158 object_get_props_from_offsets(&component->element.object,
159 component_offsets,props);
162 static void
163 component_set_props(Component *component, GPtrArray *props)
165 object_set_props_from_offsets(&component->element.object,
166 component_offsets, props);
167 apply_textattr_properties(props,component->text,"text",&component->attrs);
168 g_free(component->st_stereotype);
169 component->st_stereotype = NULL;
170 component_update_data(component);
173 static real
174 component_distance_from(Component *cmp, Point *point)
176 DiaObject *obj = &cmp->element.object;
177 return distance_rectangle_point(&obj->bounding_box, point);
180 static void
181 component_select(Component *cmp, Point *clicked_point,
182 DiaRenderer *interactive_renderer)
184 text_set_cursor(cmp->text, clicked_point, interactive_renderer);
185 text_grab_focus(cmp->text, &cmp->element.object);
186 element_update_handles(&cmp->element);
189 static ObjectChange*
190 component_move_handle(Component *cmp, Handle *handle,
191 Point *to, ConnectionPoint *cp,
192 HandleMoveReason reason, ModifierKeys modifiers)
194 assert(cmp!=NULL);
195 assert(handle!=NULL);
196 assert(to!=NULL);
198 assert(handle->id < 8);
200 return NULL;
203 static ObjectChange*
204 component_move(Component *cmp, Point *to)
206 cmp->element.corner = *to;
208 component_update_data(cmp);
210 return NULL;
213 static void
214 component_draw(Component *cmp, DiaRenderer *renderer)
216 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
217 Element *elem;
218 real x, y, w, h;
219 Point p1, p2;
221 assert(cmp != NULL);
222 assert(renderer != NULL);
224 elem = &cmp->element;
226 x = elem->corner.x;
227 y = elem->corner.y;
228 w = elem->width;
229 h = elem->height;
231 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
232 renderer_ops->set_linewidth(renderer, COMPONENT_BORDERWIDTH);
233 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
235 p1.x = x + COMPONENT_CWIDTH/2; p1.y = y;
236 p2.x = x+w; p2.y = y+h;
238 renderer_ops->fill_rect(renderer,
239 &p1, &p2,
240 &cmp->fill_color);
241 renderer_ops->draw_rect(renderer,
242 &p1, &p2,
243 &cmp->line_color);
245 p1.x= x; p1.y = y +(h - 3*COMPONENT_CHEIGHT)/2.0;
246 p2.x = x+COMPONENT_CWIDTH; p2.y = p1.y + COMPONENT_CHEIGHT;
248 renderer_ops->fill_rect(renderer,
249 &p1, &p2,
250 &cmp->fill_color);
252 renderer_ops->draw_rect(renderer,
253 &p1, &p2,
254 &cmp->line_color);
256 p1.y = p2.y + COMPONENT_CHEIGHT;
257 p2.y = p1.y + COMPONENT_CHEIGHT;
259 renderer_ops->fill_rect(renderer,
260 &p1, &p2,
261 &cmp->fill_color);
263 renderer_ops->draw_rect(renderer,
264 &p1, &p2,
265 &cmp->line_color);
267 if (cmp->st_stereotype != NULL &&
268 cmp->st_stereotype[0] != '\0') {
269 p1 = cmp->text->position;
270 p1.y -= cmp->text->height;
271 renderer_ops->set_font(renderer, cmp->text->font, cmp->text->height);
272 renderer_ops->draw_string(renderer, cmp->st_stereotype, &p1,
273 ALIGN_LEFT, &cmp->attrs.color);
276 text_draw(cmp->text, renderer);
279 static void
280 component_update_data(Component *cmp)
282 Element *elem = &cmp->element;
283 DiaObject *obj = &elem->object;
284 Point p;
285 real cw2, ch;
287 cmp->stereotype = remove_stereotype_from_string(cmp->stereotype);
288 if (!cmp->st_stereotype) {
289 cmp->st_stereotype = string_to_stereotype(cmp->stereotype);
292 text_calc_boundingbox(cmp->text, NULL);
293 elem->width = cmp->text->max_width + 2*COMPONENT_MARGIN_X + COMPONENT_CWIDTH;
294 elem->width = MAX(elem->width, 2*COMPONENT_CWIDTH);
295 elem->height = cmp->text->height*cmp->text->numlines +
296 cmp->text->descent + 0.1 + 2*COMPONENT_MARGIN_Y ;
297 elem->height = MAX(elem->height, 5*COMPONENT_CHEIGHT);
299 p = elem->corner;
300 p.x += COMPONENT_CWIDTH + COMPONENT_MARGIN_X;
301 p.y += COMPONENT_CHEIGHT;
302 p.y += cmp->text->ascent;
303 if (cmp->stereotype &&
304 cmp->stereotype[0] != '\0') {
305 p.y += cmp->text->height;
307 text_set_position(cmp->text, &p);
309 if (cmp->st_stereotype &&
310 cmp->st_stereotype[0] != '\0') {
311 DiaFont *font;
312 font = cmp->text->font;
313 elem->height += cmp->text->height;
314 elem->width = MAX(elem->width, dia_font_string_width(cmp->st_stereotype,
315 font, cmp->text->height) +
316 2*COMPONENT_MARGIN_X + COMPONENT_CWIDTH);
319 cw2 = COMPONENT_CWIDTH/2;
320 ch = COMPONENT_CHEIGHT;
321 /* Update connections: */
322 connpoint_update(&cmp->connections[0],
323 elem->corner.x + cw2,
324 elem->corner.y,
325 DIR_NORTH|DIR_WEST);
326 connpoint_update(&cmp->connections[1],
327 elem->corner.x + cw2 + (elem->width - cw2) / 2,
328 elem->corner.y,
329 DIR_NORTH);
330 connpoint_update(&cmp->connections[2],
331 elem->corner.x + elem->width,
332 elem->corner.y,
333 DIR_NORTH|DIR_EAST);
334 connpoint_update(&cmp->connections[3],
335 elem->corner.x + cw2,
336 elem->corner.y + elem->height / 2.0,
337 DIR_WEST);
338 connpoint_update(&cmp->connections[4],
339 elem->corner.x + elem->width,
340 elem->corner.y + elem->height / 2.0,
341 DIR_EAST);
342 connpoint_update(&cmp->connections[5],
343 elem->corner.x + cw2,
344 elem->corner.y + elem->height,
345 DIR_SOUTH|DIR_WEST);
346 connpoint_update(&cmp->connections[6],
347 elem->corner.x + cw2 + (elem->width - cw2)/2,
348 elem->corner.y + elem->height,
349 DIR_SOUTH);
350 connpoint_update(&cmp->connections[7],
351 elem->corner.x + elem->width,
352 elem->corner.y + elem->height,
353 DIR_SOUTH|DIR_EAST);
354 connpoint_update(&cmp->connections[8],
355 elem->corner.x,
356 elem->corner.y + elem->height / 2.0 - ch,
357 DIR_WEST);
358 connpoint_update(&cmp->connections[9],
359 elem->corner.x,
360 elem->corner.y + elem->height / 2.0 + ch,
361 DIR_WEST);
362 connpoint_update(&cmp->connections[10],
363 elem->corner.x + (elem->width-cw2)/2,
364 elem->corner.y + elem->height / 2.0 + ch,
365 DIR_ALL);
367 element_update_boundingbox(elem);
369 obj->position = elem->corner;
371 element_update_handles(elem);
374 static DiaObject *
375 component_create(Point *startpoint,
376 void *user_data,
377 Handle **handle1,
378 Handle **handle2)
380 Component *cmp;
381 Element *elem;
382 DiaObject *obj;
383 Point p;
384 DiaFont *font;
385 int i;
387 cmp = g_malloc0(sizeof(Component));
388 elem = &cmp->element;
389 obj = &elem->object;
391 obj->type = &component_type;
393 obj->ops = &component_ops;
395 obj->flags |= DIA_OBJECT_CAN_PARENT;
397 elem->corner = *startpoint;
398 cmp->line_color = attributes_get_foreground();
399 cmp->fill_color = attributes_get_background();
401 font = dia_font_new_from_style (DIA_FONT_SANS, 0.8);
402 p = *startpoint;
403 p.x += COMPONENT_CWIDTH + COMPONENT_MARGIN_X;
404 p.y += 2*COMPONENT_CHEIGHT;
406 cmp->text = new_text("", font, 0.8, &p, &color_black, ALIGN_LEFT);
407 text_get_attributes(cmp->text,&cmp->attrs);
409 dia_font_unref(font);
411 element_init(elem, 8, NUM_CONNECTIONS);
413 for (i=0;i<NUM_CONNECTIONS;i++) {
414 obj->connections[i] = &cmp->connections[i];
415 cmp->connections[i].object = obj;
416 cmp->connections[i].connected = NULL;
418 cmp->connections[10].flags = CP_FLAGS_MAIN;
419 elem->extra_spacing.border_trans = COMPONENT_BORDERWIDTH/2.0;
420 cmp->stereotype = NULL;
421 cmp->st_stereotype = NULL;
422 component_update_data(cmp);
424 for (i=0;i<8;i++) {
425 obj->handles[i]->type = HANDLE_NON_MOVABLE;
428 *handle1 = NULL;
429 *handle2 = NULL;
430 return &cmp->element.object;
433 static void
434 component_destroy(Component *cmp)
436 text_destroy(cmp->text);
437 g_free(cmp->stereotype);
438 g_free(cmp->st_stereotype);
439 element_destroy(&cmp->element);
442 static DiaObject *
443 component_load(ObjectNode obj_node, int version, const char *filename)
445 return object_load_using_properties(&component_type,
446 obj_node,version,filename);