2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / ER / attribute.c
blobf996b7aa7e8f8ad9ee523c1088a1c508e089fbab
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 /* DO NOT USE THIS OBJECT AS A BASIS FOR A NEW OBJECT. */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <assert.h>
26 #include <math.h>
27 #include <string.h>
29 #include "intl.h"
30 #include "object.h"
31 #include "element.h"
32 #include "connectionpoint.h"
33 #include "diarenderer.h"
34 #include "attributes.h"
35 #include "widgets.h"
36 #include "properties.h"
38 #include "pixmaps/attribute.xpm"
40 #define DEFAULT_WIDTH 2.0
41 #define DEFAULT_HEIGHT 1.0
42 #define FONT_HEIGHT 0.8
43 #define MULTIVALUE_BORDER_WIDTH_X 0.4
44 #define MULTIVALUE_BORDER_WIDTH_Y 0.2
45 #define TEXT_BORDER_WIDTH_X 1.0
46 #define TEXT_BORDER_WIDTH_Y 0.5
48 #define NUM_CONNECTIONS 9
50 typedef struct _Attribute Attribute;
52 struct _AttributeState {
53 ObjectState obj_state;
55 gchar *name;
56 real name_width;
58 gboolean key;
59 gboolean weakkey;
60 gboolean derived;
61 gboolean multivalue;
63 real border_width;
64 Color border_color;
65 Color inner_color;
68 struct _Attribute {
69 Element element;
71 DiaFont *font;
72 real font_height;
73 gchar *name;
74 real name_width;
76 ConnectionPoint connections[NUM_CONNECTIONS];
78 gboolean key;
79 gboolean weakkey;
80 gboolean derived;
81 gboolean multivalue;
83 real border_width;
84 Color border_color;
85 Color inner_color;
88 static real attribute_distance_from(Attribute *attribute, Point *point);
89 static void attribute_select(Attribute *attribute, Point *clicked_point,
90 DiaRenderer *interactive_renderer);
91 static ObjectChange* attribute_move_handle(Attribute *attribute, Handle *handle,
92 Point *to, ConnectionPoint *cp,
93 HandleMoveReason reason,
94 ModifierKeys modifiers);
95 static ObjectChange* attribute_move(Attribute *attribute, Point *to);
96 static void attribute_draw(Attribute *attribute, DiaRenderer *renderer);
97 static void attribute_update_data(Attribute *attribute);
98 static DiaObject *attribute_create(Point *startpoint,
99 void *user_data,
100 Handle **handle1,
101 Handle **handle2);
102 static void attribute_destroy(Attribute *attribute);
103 static DiaObject *attribute_copy(Attribute *attribute);
104 static PropDescription *
105 attribute_describe_props(Attribute *attribute);
106 static void
107 attribute_get_props(Attribute *attribute, GPtrArray *props);
108 static void
109 attribute_set_props(Attribute *attribute, GPtrArray *props);
111 static void attribute_save(Attribute *attribute, ObjectNode obj_node,
112 const char *filename);
113 static DiaObject *attribute_load(ObjectNode obj_node, int version,
114 const char *filename);
116 static ObjectTypeOps attribute_type_ops =
118 (CreateFunc) attribute_create,
119 (LoadFunc) attribute_load,
120 (SaveFunc) attribute_save
123 DiaObjectType attribute_type =
125 "ER - Attribute", /* name */
126 0, /* version */
127 (char **) attribute_xpm, /* pixmap */
129 &attribute_type_ops /* ops */
132 DiaObjectType *_attribute_type = (DiaObjectType *) &attribute_type;
134 static ObjectOps attribute_ops = {
135 (DestroyFunc) attribute_destroy,
136 (DrawFunc) attribute_draw,
137 (DistanceFunc) attribute_distance_from,
138 (SelectFunc) attribute_select,
139 (CopyFunc) attribute_copy,
140 (MoveFunc) attribute_move,
141 (MoveHandleFunc) attribute_move_handle,
142 (GetPropertiesFunc) object_create_props_dialog,
143 (ApplyPropertiesFunc) object_apply_props_from_dialog,
144 (ObjectMenuFunc) NULL,
145 (DescribePropsFunc) attribute_describe_props,
146 (GetPropsFunc) attribute_get_props,
147 (SetPropsFunc) attribute_set_props,
150 static PropDescription attribute_props[] = {
151 ELEMENT_COMMON_PROPERTIES,
152 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
153 N_("Name:"), NULL, NULL },
154 { "key", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
155 N_("Key:"), NULL, NULL },
156 { "weakkey", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
157 N_("Weak key:"), NULL, NULL },
158 { "derived", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
159 N_("Derived:"), NULL, NULL },
160 { "multivalue", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
161 N_("Multivalue:"), NULL, NULL },
162 PROP_STD_LINE_WIDTH,
163 PROP_STD_LINE_COLOUR,
164 PROP_STD_FILL_COLOUR,
165 PROP_STD_TEXT_FONT,
166 PROP_STD_TEXT_HEIGHT,
167 PROP_DESC_END
170 static PropDescription *
171 attribute_describe_props(Attribute *attribute)
173 if (attribute_props[0].quark == 0)
174 prop_desc_list_calculate_quarks(attribute_props);
175 return attribute_props;
178 static PropOffset attribute_offsets[] = {
179 ELEMENT_COMMON_PROPERTIES_OFFSETS,
180 { "name", PROP_TYPE_STRING, offsetof(Attribute, name) },
181 { "key", PROP_TYPE_BOOL, offsetof(Attribute, key) },
182 { "weakkey", PROP_TYPE_BOOL, offsetof(Attribute, weakkey) },
183 { "derived", PROP_TYPE_BOOL, offsetof(Attribute, derived) },
184 { "multivalue", PROP_TYPE_BOOL, offsetof(Attribute, multivalue) },
185 { "line_width", PROP_TYPE_REAL, offsetof(Attribute, border_width) },
186 { "line_colour", PROP_TYPE_COLOUR, offsetof(Attribute, border_color) },
187 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Attribute, inner_color) },
188 { "text_font", PROP_TYPE_FONT, offsetof(Attribute, font) },
189 { "text_height", PROP_TYPE_REAL, offsetof(Attribute, font_height) },
190 { NULL, 0, 0}
194 static void
195 attribute_get_props(Attribute *attribute, GPtrArray *props)
197 object_get_props_from_offsets(&attribute->element.object,
198 attribute_offsets, props);
201 static void
202 attribute_set_props(Attribute *attribute, GPtrArray *props)
204 object_set_props_from_offsets(&attribute->element.object,
205 attribute_offsets, props);
206 attribute_update_data(attribute);
209 static real
210 attribute_distance_from(Attribute *attribute, Point *point)
212 Element *elem = &attribute->element;
213 Point center;
215 center.x = elem->corner.x+elem->width/2;
216 center.y = elem->corner.y+elem->height/2;
218 return distance_ellipse_point(&center, elem->width, elem->height,
219 attribute->border_width, point);
222 static void
223 attribute_select(Attribute *attribute, Point *clicked_point,
224 DiaRenderer *interactive_renderer)
226 element_update_handles(&attribute->element);
229 static ObjectChange*
230 attribute_move_handle(Attribute *attribute, Handle *handle,
231 Point *to, ConnectionPoint *cp,
232 HandleMoveReason reason, ModifierKeys modifiers)
234 assert(attribute!=NULL);
235 assert(handle!=NULL);
236 assert(to!=NULL);
238 assert(handle->id < 8);
239 element_move_handle(&attribute->element, handle->id, to, cp,
240 reason, modifiers);
241 attribute_update_data(attribute);
243 return NULL;
246 static ObjectChange*
247 attribute_move(Attribute *attribute, Point *to)
249 attribute->element.corner = *to;
250 attribute_update_data(attribute);
252 return NULL;
255 static void
256 attribute_draw(Attribute *attribute, DiaRenderer *renderer)
258 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
259 Point center;
260 Point start, end;
261 Point p;
262 Element *elem;
263 real width;
265 assert(attribute != NULL);
266 assert(renderer != NULL);
268 elem = &attribute->element;
270 center.x = elem->corner.x + elem->width/2;
271 center.y = elem->corner.y + elem->height/2;
273 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
274 renderer_ops->fill_ellipse(renderer, &center,
275 elem->width, elem->height,
276 &attribute->inner_color);
278 renderer_ops->set_linewidth(renderer, attribute->border_width);
279 if (attribute->derived) {
280 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
281 renderer_ops->set_dashlength(renderer, 0.3);
282 } else {
283 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
286 renderer_ops->draw_ellipse(renderer, &center,
287 elem->width, elem->height,
288 &attribute->border_color);
290 if(attribute->multivalue) {
291 renderer_ops->draw_ellipse(renderer, &center,
292 elem->width - 2*MULTIVALUE_BORDER_WIDTH_X,
293 elem->height - 2*MULTIVALUE_BORDER_WIDTH_Y,
294 &attribute->border_color);
297 p.x = elem->corner.x + elem->width / 2.0;
298 p.y = elem->corner.y + (elem->height - attribute->font_height)/2.0 +
299 dia_font_ascent(attribute->name,
300 attribute->font, attribute->font_height);
302 renderer_ops->set_font(renderer, attribute->font, attribute->font_height);
303 renderer_ops->draw_string(renderer, attribute->name,
304 &p, ALIGN_CENTER,
305 &color_black);
307 if (attribute->key || attribute->weakkey) {
308 if (attribute->weakkey) {
309 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
310 renderer_ops->set_dashlength(renderer, 0.3);
311 } else {
312 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
314 width = dia_font_string_width(attribute->name,
315 attribute->font, attribute->font_height);
316 start.x = center.x - width / 2;
317 start.y = center.y + 0.4;
318 end.x = center.x + width / 2;
319 end.y = center.y + 0.4;
320 renderer_ops->draw_line(renderer, &start, &end, &color_black);
324 static void
325 attribute_update_data(Attribute *attribute)
327 Element *elem = &attribute->element;
328 DiaObject *obj = &elem->object;
329 Point center;
330 ElementBBExtras *extra = &elem->extra_spacing;
331 real half_x, half_y;
333 attribute->name_width =
334 dia_font_string_width(attribute->name,
335 attribute->font, attribute->font_height);
337 elem->width = attribute->name_width + 2*TEXT_BORDER_WIDTH_X;
338 elem->height = attribute->font_height + 2*TEXT_BORDER_WIDTH_Y;
340 center.x = elem->corner.x + elem->width / 2.0;
341 center.y = elem->corner.y + elem->height / 2.0;
343 half_x = elem->width * M_SQRT1_2 / 2.0;
344 half_y = elem->height * M_SQRT1_2 / 2.0;
346 /* Update connections: */
347 connpoint_update(&attribute->connections[0],
348 center.x - half_x,
349 center.y - half_y,
350 DIR_NORTHWEST);
351 connpoint_update(&attribute->connections[1],
352 center.x,
353 elem->corner.y,
354 DIR_NORTH);
355 connpoint_update(&attribute->connections[2],
356 center.x + half_x,
357 center.y - half_y,
358 DIR_NORTHEAST);
359 connpoint_update(&attribute->connections[3],
360 elem->corner.x,
361 center.y,
362 DIR_WEST);
363 connpoint_update(&attribute->connections[4],
364 elem->corner.x + elem->width,
365 elem->corner.y + elem->height / 2.0,
366 DIR_EAST);
367 connpoint_update(&attribute->connections[5],
368 center.x - half_x,
369 center.y + half_y,
370 DIR_SOUTHWEST);
371 connpoint_update(&attribute->connections[6],
372 elem->corner.x + elem->width / 2.0,
373 elem->corner.y + elem->height,
374 DIR_SOUTH);
375 connpoint_update(&attribute->connections[7],
376 center.x + half_x,
377 center.y + half_y,
378 DIR_SOUTHEAST);
379 connpoint_update(&attribute->connections[8],
380 center.x,
381 center.y,
382 DIR_ALL);
384 extra->border_trans = attribute->border_width/2.0;
385 element_update_boundingbox(elem);
387 obj->position = elem->corner;
389 element_update_handles(elem);
393 static DiaObject *
394 attribute_create(Point *startpoint,
395 void *user_data,
396 Handle **handle1,
397 Handle **handle2)
399 Attribute *attribute;
400 Element *elem;
401 DiaObject *obj;
402 int i;
404 attribute = g_malloc0(sizeof(Attribute));
405 elem = &attribute->element;
406 obj = &elem->object;
408 obj->type = &attribute_type;
409 obj->ops = &attribute_ops;
411 elem->corner = *startpoint;
412 elem->width = DEFAULT_WIDTH;
413 elem->height = DEFAULT_HEIGHT;
415 attribute->border_width = attributes_get_default_linewidth();
416 attribute->border_color = attributes_get_foreground();
417 attribute->inner_color = attributes_get_background();
419 element_init(elem, 8, NUM_CONNECTIONS);
421 for (i=0;i<NUM_CONNECTIONS;i++) {
422 obj->connections[i] = &attribute->connections[i];
423 attribute->connections[i].object = obj;
424 attribute->connections[i].connected = NULL;
426 attribute->connections[8].flags = CP_FLAGS_MAIN;
428 attribute->key = FALSE;
429 attribute->weakkey = FALSE;
430 attribute->derived = FALSE;
431 attribute->multivalue = FALSE;
432 attribute->font = dia_font_new_from_style(DIA_FONT_MONOSPACE,FONT_HEIGHT);
433 attribute->font_height = FONT_HEIGHT;
434 attribute->name = g_strdup(_("Attribute"));
436 attribute->name_width =
437 dia_font_string_width(attribute->name,
438 attribute->font, attribute->font_height);
440 attribute_update_data(attribute);
442 for (i=0;i<8;i++) {
443 obj->handles[i]->type = HANDLE_NON_MOVABLE;
446 *handle1 = NULL;
447 *handle2 = obj->handles[0];
448 return &attribute->element.object;
451 static void
452 attribute_destroy(Attribute *attribute)
454 element_destroy(&attribute->element);
455 g_free(attribute->name);
458 static DiaObject *
459 attribute_copy(Attribute *attribute)
461 int i;
462 Attribute *newattribute;
463 Element *elem, *newelem;
464 DiaObject *newobj;
466 elem = &attribute->element;
468 newattribute = g_malloc0(sizeof(Attribute));
469 newelem = &newattribute->element;
470 newobj = &newelem->object;
472 element_copy(elem, newelem);
474 newattribute->border_width = attribute->border_width;
475 newattribute->border_color = attribute->border_color;
476 newattribute->inner_color = attribute->inner_color;
478 for (i=0;i<NUM_CONNECTIONS;i++) {
479 newobj->connections[i] = &newattribute->connections[i];
480 newattribute->connections[i].object = newobj;
481 newattribute->connections[i].connected = NULL;
482 newattribute->connections[i].pos = attribute->connections[i].pos;
483 newattribute->connections[i].last_pos = attribute->connections[i].last_pos;
484 newattribute->connections[i].flags = attribute->connections[i].flags;
487 newattribute->font = dia_font_ref(attribute->font);
488 newattribute->font_height = attribute->font_height;
489 newattribute->name = g_strdup(attribute->name);
490 newattribute->name_width = attribute->name_width;
492 newattribute->key = attribute->key;
493 newattribute->weakkey = attribute->weakkey;
494 newattribute->derived = attribute->derived;
495 newattribute->multivalue = attribute->multivalue;
497 return &newattribute->element.object;
501 static void
502 attribute_save(Attribute *attribute, ObjectNode obj_node,
503 const char *filename)
505 element_save(&attribute->element, obj_node);
507 data_add_real(new_attribute(obj_node, "border_width"),
508 attribute->border_width);
509 data_add_color(new_attribute(obj_node, "border_color"),
510 &attribute->border_color);
511 data_add_color(new_attribute(obj_node, "inner_color"),
512 &attribute->inner_color);
513 data_add_string(new_attribute(obj_node, "name"),
514 attribute->name);
515 data_add_boolean(new_attribute(obj_node, "key"),
516 attribute->key);
517 data_add_boolean(new_attribute(obj_node, "weak_key"),
518 attribute->weakkey);
519 data_add_boolean(new_attribute(obj_node, "derived"),
520 attribute->derived);
521 data_add_boolean(new_attribute(obj_node, "multivalued"),
522 attribute->multivalue);
523 data_add_font (new_attribute (obj_node, "font"),
524 attribute->font);
525 data_add_real(new_attribute(obj_node, "font_height"),
526 attribute->font_height);
529 static DiaObject *attribute_load(ObjectNode obj_node, int version,
530 const char *filename)
532 Attribute *attribute;
533 Element *elem;
534 DiaObject *obj;
535 int i;
536 AttributeNode attr;
538 attribute = g_malloc0(sizeof(Attribute));
539 elem = &attribute->element;
540 obj = &elem->object;
542 obj->type = &attribute_type;
543 obj->ops = &attribute_ops;
545 element_load(elem, obj_node);
547 attribute->border_width = 0.1;
548 attr = object_find_attribute(obj_node, "border_width");
549 if (attr != NULL)
550 attribute->border_width = data_real( attribute_first_data(attr) );
552 attribute->border_color = color_black;
553 attr = object_find_attribute(obj_node, "border_color");
554 if (attr != NULL)
555 data_color(attribute_first_data(attr), &attribute->border_color);
557 attribute->inner_color = color_white;
558 attr = object_find_attribute(obj_node, "inner_color");
559 if (attr != NULL)
560 data_color(attribute_first_data(attr), &attribute->inner_color);
562 attribute->name = NULL;
563 attr = object_find_attribute(obj_node, "name");
564 if (attr != NULL)
565 attribute->name = data_string(attribute_first_data(attr));
567 attr = object_find_attribute(obj_node, "key");
568 if (attr != NULL)
569 attribute->key = data_boolean(attribute_first_data(attr));
571 attr = object_find_attribute(obj_node, "weak_key");
572 if (attr != NULL)
573 attribute->weakkey = data_boolean(attribute_first_data(attr));
575 attr = object_find_attribute(obj_node, "derived");
576 if (attr != NULL)
577 attribute->derived = data_boolean(attribute_first_data(attr));
579 attr = object_find_attribute(obj_node, "multivalued");
580 if (attr != NULL)
581 attribute->multivalue = data_boolean(attribute_first_data(attr));
583 if (attribute->font != NULL) {
584 /* This shouldn't happen, but doesn't hurt */
585 dia_font_unref(attribute->font);
586 attribute->font = NULL;
588 attr = object_find_attribute (obj_node, "font");
589 if (attr != NULL)
590 attribute->font = data_font (attribute_first_data (attr));
592 attribute->font_height = FONT_HEIGHT;
593 attr = object_find_attribute (obj_node, "font_height");
594 if (attr != NULL)
595 attribute->font_height = data_real( attribute_first_data(attr) );
597 element_init(elem, 8, NUM_CONNECTIONS);
599 for (i=0;i<NUM_CONNECTIONS;i++) {
600 obj->connections[i] = &attribute->connections[i];
601 attribute->connections[i].object = obj;
602 attribute->connections[i].connected = NULL;
604 attribute->connections[8].flags = CP_FLAGS_MAIN;
606 if (attribute->font == NULL) {
607 attribute->font = dia_font_new_from_style(DIA_FONT_MONOSPACE,
608 attribute->font_height);
611 attribute->name_width = dia_font_string_width(attribute->name,
612 attribute->font,
613 attribute->font_height);
614 attribute_update_data(attribute);
616 for (i=0;i<8;i++) {
617 obj->handles[i]->type = HANDLE_NON_MOVABLE;
620 return &attribute->element.object;