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