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
19 /* DO NOT USE THIS OBJECT AS A BASIS FOR A NEW OBJECT. */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <assert.h>
26 #include <math.h>
27 #include <string.h>
28
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"
37
38 #include "pixmaps/entity.xpm"
39
40 #define DEFAULT_WIDTH 2.0
41 #define DEFAULT_HEIGHT 1.0
42 #define TEXT_BORDER_WIDTH_X 0.7
43 #define TEXT_BORDER_WIDTH_Y 0.5
44 #define WEAK_BORDER_WIDTH 0.25
45 #define FONT_HEIGHT 0.8
46 #define DIAMOND_RATIO 0.6
47
48 typedef struct _Entity Entity;
49
50 #define NUM_CONNECTIONS 9
51
52 struct _Entity {
53 Element element;
54
55 ConnectionPoint connections[NUM_CONNECTIONS];
56
57 real border_width;
58 Color border_color;
59 Color inner_color;
60
61 gboolean associative;
62
63 DiaFont *font;
64 real font_height;
65 char *name;
66 real name_width;
67
68 int weak;
69
70 };
71
72 static real entity_distance_from(Entity *entity, Point *point);
73 static void entity_select(Entity *entity, Point *clicked_point,
74 DiaRenderer *interactive_renderer);
75 static ObjectChange* entity_move_handle(Entity *entity, Handle *handle,
76 Point *to, ConnectionPoint *cp,
77 HandleMoveReason reason,
78 ModifierKeys modifiers);
79 static ObjectChange* entity_move(Entity *entity, Point *to);
80 static void entity_draw(Entity *entity, DiaRenderer *renderer);
81 static void entity_update_data(Entity *entity);
82 static DiaObject *entity_create(Point *startpoint,
83 void *user_data,
84 Handle **handle1,
85 Handle **handle2);
86 static void entity_destroy(Entity *entity);
87 static DiaObject *entity_copy(Entity *entity);
88 static PropDescription *
89 entity_describe_props(Entity *entity);
90 static void entity_get_props(Entity *entity, GPtrArray *props);
91 static void entity_set_props(Entity *entity, GPtrArray *props);
92
93 static void entity_save(Entity *entity, ObjectNode obj_node,
94 const char *filename);
95 static DiaObject *entity_load(ObjectNode obj_node, int version,
96 const char *filename);
97
98 static ObjectTypeOps entity_type_ops =
99 {
100 (CreateFunc) entity_create,
101 (LoadFunc) entity_load,
102 (SaveFunc) entity_save
103 };
104
105 DiaObjectType entity_type =
106 {
107 "ER - Entity", /* name */
108 0, /* version */
109 (char **) entity_xpm, /* pixmap */
110
111 &entity_type_ops /* ops */
112 };
113
114 DiaObjectType *_entity_type = (DiaObjectType *) &entity_type;
115
116 static ObjectOps entity_ops = {
117 (DestroyFunc) entity_destroy,
118 (DrawFunc) entity_draw,
119 (DistanceFunc) entity_distance_from,
120 (SelectFunc) entity_select,
121 (CopyFunc) entity_copy,
122 (MoveFunc) entity_move,
123 (MoveHandleFunc) entity_move_handle,
124 (GetPropertiesFunc) object_create_props_dialog,
125 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
126 (ObjectMenuFunc) NULL,
127 (DescribePropsFunc) entity_describe_props,
128 (GetPropsFunc) entity_get_props,
129 (SetPropsFunc) entity_set_props,
130 (TextEditFunc) 0,
131 (ApplyPropertiesListFunc) object_apply_props,
132 };
133
134 static PropDescription entity_props[] = {
135 ELEMENT_COMMON_PROPERTIES,
136 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
137 N_("Name:"), NULL, NULL },
138 { "weak", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_NO_DEFAULTS,
139 N_("Weak:"), NULL, NULL },
140 { "associative", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
141 N_("Associative:"), NULL, NULL },
142 PROP_STD_LINE_WIDTH,
143 PROP_STD_LINE_COLOUR,
144 PROP_STD_FILL_COLOUR,
145 PROP_STD_TEXT_FONT,
146 PROP_STD_TEXT_HEIGHT,
147 PROP_DESC_END
148 };
149
150 static PropDescription *
entity_describe_props(Entity * entity)151 entity_describe_props(Entity *entity)
152 {
153 if (entity_props[0].quark == 0)
154 prop_desc_list_calculate_quarks(entity_props);
155 return entity_props;
156 }
157
158 static PropOffset entity_offsets[] = {
159 ELEMENT_COMMON_PROPERTIES_OFFSETS,
160 { "name", PROP_TYPE_STRING, offsetof(Entity, name) },
161 { "weak", PROP_TYPE_BOOL, offsetof(Entity, weak) },
162 { "associative", PROP_TYPE_BOOL, offsetof(Entity, associative) },
163 { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Entity, border_width) },
164 { "line_colour", PROP_TYPE_COLOUR, offsetof(Entity, border_color) },
165 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Entity, inner_color) },
166 { "text_font", PROP_TYPE_FONT, offsetof (Entity, font) },
167 { PROP_STDNAME_TEXT_HEIGHT, PROP_STDTYPE_TEXT_HEIGHT, offsetof(Entity, font_height) },
168 { NULL, 0, 0}
169 };
170
171
172 static void
entity_get_props(Entity * entity,GPtrArray * props)173 entity_get_props(Entity *entity, GPtrArray *props)
174 {
175 object_get_props_from_offsets(&entity->element.object,
176 entity_offsets, props);
177 }
178
179 static void
entity_set_props(Entity * entity,GPtrArray * props)180 entity_set_props(Entity *entity, GPtrArray *props)
181 {
182 object_set_props_from_offsets(&entity->element.object,
183 entity_offsets, props);
184 entity_update_data(entity);
185 }
186
187 static real
entity_distance_from(Entity * entity,Point * point)188 entity_distance_from(Entity *entity, Point *point)
189 {
190 Element *elem = &entity->element;
191 Rectangle rect;
192
193 rect.left = elem->corner.x - entity->border_width/2;
194 rect.right = elem->corner.x + elem->width + entity->border_width/2;
195 rect.top = elem->corner.y - entity->border_width/2;
196 rect.bottom = elem->corner.y + elem->height + entity->border_width/2;
197 return distance_rectangle_point(&rect, point);
198 }
199
200 static void
entity_select(Entity * entity,Point * clicked_point,DiaRenderer * interactive_renderer)201 entity_select(Entity *entity, Point *clicked_point,
202 DiaRenderer *interactive_renderer)
203 {
204 element_update_handles(&entity->element);
205 }
206
207 static ObjectChange*
entity_move_handle(Entity * entity,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)208 entity_move_handle(Entity *entity, Handle *handle,
209 Point *to, ConnectionPoint *cp,
210 HandleMoveReason reason, ModifierKeys modifiers)
211 {
212 assert(entity!=NULL);
213 assert(handle!=NULL);
214 assert(to!=NULL);
215
216 element_move_handle(&entity->element, handle->id, to, cp, reason, modifiers);
217
218 entity_update_data(entity);
219
220 return NULL;
221 }
222
223 static ObjectChange*
entity_move(Entity * entity,Point * to)224 entity_move(Entity *entity, Point *to)
225 {
226 entity->element.corner = *to;
227
228 entity_update_data(entity);
229
230 return NULL;
231 }
232
233 static void
entity_draw(Entity * entity,DiaRenderer * renderer)234 entity_draw(Entity *entity, DiaRenderer *renderer)
235 {
236 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
237 Point ul_corner, lr_corner;
238 Point p;
239 Element *elem;
240 coord diff;
241
242 assert(entity != NULL);
243 assert(renderer != NULL);
244
245 elem = &entity->element;
246
247 ul_corner.x = elem->corner.x;
248 ul_corner.y = elem->corner.y;
249 lr_corner.x = elem->corner.x + elem->width;
250 lr_corner.y = elem->corner.y + elem->height;
251
252 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
253
254 renderer_ops->fill_rect(renderer,
255 &ul_corner,
256 &lr_corner,
257 &entity->inner_color);
258
259 renderer_ops->set_linewidth(renderer, entity->border_width);
260 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
261 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
262
263 renderer_ops->draw_rect(renderer,
264 &ul_corner,
265 &lr_corner,
266 &entity->border_color);
267
268 if(entity->weak) {
269 diff = WEAK_BORDER_WIDTH/*MIN(elem->width / 2.0 * 0.20, elem->height / 2.0 * 0.20)*/;
270 ul_corner.x += diff;
271 ul_corner.y += diff;
272 lr_corner.x -= diff;
273 lr_corner.y -= diff;
274 renderer_ops->draw_rect(renderer,
275 &ul_corner, &lr_corner,
276 &entity->border_color);
277 }
278 if(entity->associative){
279 Point corners[4];
280 corners[0].x = elem->corner.x;
281 corners[0].y = elem->corner.y + elem->height / 2;
282 corners[1].x = elem->corner.x + elem->width / 2;
283 corners[1].y = elem->corner.y;
284 corners[2].x = elem->corner.x + elem->width;
285 corners[2].y = elem->corner.y + elem->height / 2;
286 corners[3].x = elem->corner.x + elem->width / 2;
287 corners[3].y = elem->corner.y + elem->height;
288 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
289
290 renderer_ops->fill_polygon(renderer, corners, 4,
291 &entity->inner_color);
292
293 renderer_ops->set_linewidth(renderer, entity->border_width);
294 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
295 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
296
297 renderer_ops->draw_polygon(renderer, corners, 4,
298 &entity->border_color);
299 }
300
301 p.x = elem->corner.x + elem->width / 2.0;
302 p.y = elem->corner.y + (elem->height - entity->font_height)/2.0 +
303 dia_font_ascent(entity->name,entity->font, entity->font_height);
304 renderer_ops->set_font(renderer, entity->font, entity->font_height);
305 renderer_ops->draw_string(renderer,
306 entity->name,
307 &p, ALIGN_CENTER,
308 &color_black);
309 }
310
311 static void
entity_update_data(Entity * entity)312 entity_update_data(Entity *entity)
313 {
314 Element *elem = &entity->element;
315 DiaObject *obj = &elem->object;
316 ElementBBExtras *extra = &elem->extra_spacing;
317
318 entity->name_width =
319 dia_font_string_width(entity->name, entity->font, entity->font_height);
320
321 if(entity->associative){
322 elem->width = entity->name_width + 2*TEXT_BORDER_WIDTH_X;
323 elem->height = elem->width * DIAMOND_RATIO;
324 }
325 else {
326 elem->width = entity->name_width + 2*TEXT_BORDER_WIDTH_X;
327 elem->height = entity->font_height + 2*TEXT_BORDER_WIDTH_Y;
328 }
329
330 /* Update connections: */
331 connpoint_update(&entity->connections[0],
332 elem->corner.x,
333 elem->corner.y,
334 DIR_NORTHWEST);
335 connpoint_update(&entity->connections[1],
336 elem->corner.x + elem->width / 2.0,
337 elem->corner.y,
338 DIR_NORTH);
339 connpoint_update(&entity->connections[2],
340 elem->corner.x + elem->width,
341 elem->corner.y,
342 DIR_NORTHEAST);
343 connpoint_update(&entity->connections[3],
344 elem->corner.x,
345 elem->corner.y + elem->height / 2.0,
346 DIR_WEST);
347 connpoint_update(&entity->connections[4],
348 elem->corner.x + elem->width,
349 elem->corner.y + elem->height / 2.0,
350 DIR_EAST);
351 connpoint_update(&entity->connections[5],
352 elem->corner.x,
353 elem->corner.y + elem->height,
354 DIR_SOUTHWEST);
355 connpoint_update(&entity->connections[6],
356 elem->corner.x + elem->width / 2.0,
357 elem->corner.y + elem->height,
358 DIR_SOUTH);
359 connpoint_update(&entity->connections[7],
360 elem->corner.x + elem->width,
361 elem->corner.y + elem->height,
362 DIR_SOUTHEAST);
363 connpoint_update(&entity->connections[8],
364 elem->corner.x + elem->width / 2.0,
365 elem->corner.y + elem->height / 2.0,
366 DIR_ALL);
367
368 extra->border_trans = entity->border_width/2.0;
369 element_update_boundingbox(elem);
370
371 obj->position = elem->corner;
372
373 element_update_handles(elem);
374 }
375
376 static DiaObject *
entity_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)377 entity_create(Point *startpoint,
378 void *user_data,
379 Handle **handle1,
380 Handle **handle2)
381 {
382 Entity *entity;
383 Element *elem;
384 DiaObject *obj;
385 int i;
386
387 entity = g_malloc0(sizeof(Entity));
388 elem = &entity->element;
389 obj = &elem->object;
390
391 obj->type = &entity_type;
392
393 obj->ops = &entity_ops;
394
395 elem->corner = *startpoint;
396 elem->width = DEFAULT_WIDTH;
397 elem->height = DEFAULT_HEIGHT;
398
399 entity->border_width = attributes_get_default_linewidth();
400 entity->border_color = attributes_get_foreground();
401 entity->inner_color = attributes_get_background();
402
403 element_init(elem, 8, NUM_CONNECTIONS);
404
405 for (i=0;i<NUM_CONNECTIONS;i++) {
406 obj->connections[i] = &entity->connections[i];
407 entity->connections[i].object = obj;
408 entity->connections[i].connected = NULL;
409 }
410 entity->connections[8].flags = CP_FLAGS_MAIN;
411
412 entity->weak = GPOINTER_TO_INT(user_data);
413 entity->font = dia_font_new_from_style(DIA_FONT_MONOSPACE,FONT_HEIGHT);
414 entity->font_height = FONT_HEIGHT;
415 entity->name = g_strdup(_("Entity"));
416
417 entity->name_width =
418 dia_font_string_width(entity->name, entity->font, entity->font_height);
419
420 entity_update_data(entity);
421
422 for (i=0;i<8;i++) {
423 obj->handles[i]->type = HANDLE_NON_MOVABLE;
424 }
425
426 *handle1 = NULL;
427 *handle2 = obj->handles[0];
428 return &entity->element.object;
429 }
430
431 static void
entity_destroy(Entity * entity)432 entity_destroy(Entity *entity)
433 {
434 dia_font_unref(entity->font);
435 element_destroy(&entity->element);
436 g_free(entity->name);
437 }
438
439 static DiaObject *
entity_copy(Entity * entity)440 entity_copy(Entity *entity)
441 {
442 int i;
443 Entity *newentity;
444 Element *elem, *newelem;
445 DiaObject *newobj;
446
447 elem = &entity->element;
448
449 newentity = g_malloc0(sizeof(Entity));
450 newelem = &newentity->element;
451 newobj = &newelem->object;
452
453 element_copy(elem, newelem);
454
455 newentity->border_width = entity->border_width;
456 newentity->border_color = entity->border_color;
457 newentity->inner_color = entity->inner_color;
458
459 for (i=0;i<NUM_CONNECTIONS;i++) {
460 newobj->connections[i] = &newentity->connections[i];
461 newentity->connections[i].object = newobj;
462 newentity->connections[i].connected = NULL;
463 newentity->connections[i].pos = entity->connections[i].pos;
464 newentity->connections[i].last_pos = entity->connections[i].last_pos;
465 newentity->connections[i].flags = entity->connections[i].flags;
466 }
467
468 newentity->font = dia_font_ref(entity->font);
469 newentity->font_height = entity->font_height;
470 newentity->name = g_strdup(entity->name);
471 newentity->name_width = entity->name_width;
472
473 newentity->weak = entity->weak;
474
475 return &newentity->element.object;
476 }
477
478 static void
entity_save(Entity * entity,ObjectNode obj_node,const char * filename)479 entity_save(Entity *entity, ObjectNode obj_node, const char *filename)
480 {
481 element_save(&entity->element, obj_node);
482
483 data_add_real(new_attribute(obj_node, "border_width"),
484 entity->border_width);
485 data_add_color(new_attribute(obj_node, "border_color"),
486 &entity->border_color);
487 data_add_color(new_attribute(obj_node, "inner_color"),
488 &entity->inner_color);
489 data_add_string(new_attribute(obj_node, "name"),
490 entity->name);
491 data_add_boolean(new_attribute(obj_node, "weak"),
492 entity->weak);
493 data_add_boolean(new_attribute(obj_node, "associative"),
494 entity->associative);
495 data_add_font (new_attribute (obj_node, "font"),
496 entity->font);
497 data_add_real(new_attribute(obj_node, "font_height"),
498 entity->font_height);
499 }
500
501 static DiaObject *
entity_load(ObjectNode obj_node,int version,const char * filename)502 entity_load(ObjectNode obj_node, int version, const char *filename)
503 {
504 Entity *entity;
505 Element *elem;
506 DiaObject *obj;
507 int i;
508 AttributeNode attr;
509
510 entity = g_malloc0(sizeof(Entity));
511 elem = &entity->element;
512 obj = &elem->object;
513
514 obj->type = &entity_type;
515 obj->ops = &entity_ops;
516
517 element_load(elem, obj_node);
518
519 entity->border_width = 0.1;
520 attr = object_find_attribute(obj_node, "border_width");
521 if (attr != NULL)
522 entity->border_width = data_real( attribute_first_data(attr) );
523
524 entity->border_color = color_black;
525 attr = object_find_attribute(obj_node, "border_color");
526 if (attr != NULL)
527 data_color(attribute_first_data(attr), &entity->border_color);
528
529 entity->inner_color = color_white;
530 attr = object_find_attribute(obj_node, "inner_color");
531 if (attr != NULL)
532 data_color(attribute_first_data(attr), &entity->inner_color);
533
534 entity->name = NULL;
535 attr = object_find_attribute(obj_node, "name");
536 if (attr != NULL)
537 entity->name = data_string(attribute_first_data(attr));
538
539 attr = object_find_attribute(obj_node, "weak");
540 if (attr != NULL)
541 entity->weak = data_boolean(attribute_first_data(attr));
542
543 attr = object_find_attribute(obj_node, "associative");
544 if (attr != NULL)
545 entity->associative = data_boolean(attribute_first_data(attr));
546
547 if (entity->font != NULL) {
548 /* This shouldn't happen, but doesn't hurt */
549 dia_font_unref(entity->font);
550 entity->font = NULL;
551 }
552 attr = object_find_attribute (obj_node, "font");
553 if (attr != NULL)
554 entity->font = data_font (attribute_first_data (attr));
555
556 entity->font_height = FONT_HEIGHT;
557 attr = object_find_attribute(obj_node, "font_height");
558 if (attr != NULL)
559 entity->font_height = data_real(attribute_first_data(attr));
560
561 element_init(elem, 8, NUM_CONNECTIONS);
562
563 for (i=0;i<NUM_CONNECTIONS;i++) {
564 obj->connections[i] = &entity->connections[i];
565 entity->connections[i].object = obj;
566 entity->connections[i].connected = NULL;
567 }
568 entity->connections[8].flags = CP_FLAGS_MAIN;
569
570 if (entity->font == NULL) {
571 entity->font = dia_font_new_from_style(DIA_FONT_MONOSPACE,1.0);
572 }
573
574 entity->name_width =
575 dia_font_string_width(entity->name, entity->font, entity->font_height);
576
577 entity_update_data(entity);
578
579 for (i=0;i<8;i++) {
580 obj->handles[i]->type = HANDLE_NON_MOVABLE;
581 }
582
583 return &entity->element.object;
584 }
585