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:    table.c
19  *
20  * Purpose: This file contains implementation of the "table" code.
21  */
22 
23 /*
24  * The code listed here is very similar to the one for the UML class
25  * object ... at least in its core. Indeed, I've used it as a
26  * template. -- pn
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include <assert.h>
34 #include <string.h>
35 #include <ctype.h>
36 
37 #include "database.h"
38 #include "propinternals.h"
39 #include "diarenderer.h"
40 #include "element.h"
41 #include "attributes.h"
42 #include "pixmaps/table.xpm"
43 
44 #define TABLE_UNDERLINE_WIDTH 0.05
45 #define TABLE_COMMENT_MAXWIDTH 40
46 #define TABLE_ATTR_NAME_TYPE_GAP 0.5
47 #define TABLE_ATTR_NAME_OFFSET 0.3
48 #define TABLE_ATTR_COMMENT_OFFSET 0.25
49 #define TABLE_ATTR_INDIC_WIDTH 0.20
50 #define TABLE_ATTR_INDIC_LINE_WIDTH 0.01
51 
52 /* ----------------------------------------------------------------------- */
53 
54 static real         table_calculate_namebox_data (Table *);
55 static real         table_init_attributesbox_height (Table *);
56 static DiaObject *  table_create (Point *, void *, Handle **, Handle **);
57 static DiaObject *  table_load (ObjectNode, int, const char *);
58 static void         table_save (Table *, ObjectNode, const char *);
59 static void         table_destroy (Table *);
60 static real         table_distance_from (Table *, Point *);
61 static void         table_select (Table *, Point *, DiaRenderer *);
62 static DiaObject *  table_copy (Table *table);
63 static ObjectChange* table_move (Table *, Point *);
64 static ObjectChange * table_move_handle (Table *, Handle *,
65                                          Point *, ConnectionPoint *,
66                                          HandleMoveReason, ModifierKeys);
67 static PropDescription * table_describe_props (Table *);
68 static void         table_get_props (Table *, GPtrArray *);
69 static void         table_set_props (Table *, GPtrArray *);
70 ObjectChange *_table_dialog_apply_changes (Table * table, GtkWidget * widget);
71 
72 static void         table_draw (Table *, DiaRenderer *);
73 static real         table_draw_namebox (Table *, DiaRenderer *, Element *);
74 static real         table_draw_attributesbox (Table *, DiaRenderer *,
75                                               Element *, real);
76 static DiaMenu * table_object_menu(DiaObject *, Point *);
77 static ObjectChange * table_show_comments_cb(DiaObject *, Point *, gpointer);
78 static void underline_table_attribute (DiaRenderer  *, Point,
79                                        TableAttribute *, Table *);
80 static void fill_diamond (DiaRenderer *, real, real, Point *, Color *);
81 static void table_init_fonts (Table *);
82 
83 static gchar * create_documentation_tag (gchar * comment,
84                                          gboolean tagging,
85                                          gint WrapPoint,
86                                          gint *NumberOfLines);
87 static void draw_comments(DiaRenderer *renderer,
88                           DiaFont     *font,
89                           real         font_height,
90                           Color       *text_color,
91                           gchar       *comment,
92                           gboolean     comment_tagging,
93                           gint         Comment_line_length,
94                           Point       *p,
95                           gint         alignment);
96 
97 /* ----------------------------------------------------------------------- */
98 
99 static ObjectTypeOps table_type_ops =
100   {
101     (CreateFunc) table_create,
102     (LoadFunc) table_load,
103     (SaveFunc) table_save
104   };
105 
106 DiaObjectType table_type =
107   {
108     "Database - Table",   /* name */
109     0,               /* version */
110     (char **) table_xpm, /* pixmap */
111     &table_type_ops, /* operations */
112     NULL,
113     NULL
114   };
115 
116 static ObjectOps table_ops =
117   {
118     (DestroyFunc)               table_destroy,
119     (DrawFunc)                  table_draw,
120     (DistanceFunc)              table_distance_from,
121     (SelectFunc)                table_select,
122     (CopyFunc)                  table_copy,
123     (MoveFunc)                  table_move,
124     (MoveHandleFunc)            table_move_handle,
125     (GetPropertiesFunc)         table_get_properties_dialog,
126     (ApplyPropertiesDialogFunc) _table_dialog_apply_changes,
127     (ObjectMenuFunc)            table_object_menu,
128     (DescribePropsFunc)         table_describe_props,
129     (GetPropsFunc)              table_get_props,
130     (SetPropsFunc)              table_set_props,
131     (TextEditFunc) 0,
132     (ApplyPropertiesListFunc) object_apply_props,
133 };
134 
135 static PropDescription table_attribute_props[] =
136   {
137     { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
138       N_("Name"), NULL, NULL },
139     { "type", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
140       N_("Type"), NULL, NULL },
141     { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
142       N_("Comment"), NULL, NULL },
143     { "primary_key", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
144       N_("Primary key"), NULL, NULL },
145     { "nullable", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
146       N_("Nullable"), NULL, NULL },
147     { "unique", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
148       N_("Unique"), NULL, NULL },
149 
150     PROP_DESC_END
151   };
152 
153 static PropOffset table_attribute_offsets[] = {
154   { "name", PROP_TYPE_STRING, offsetof(TableAttribute, name) },
155   { "type", PROP_TYPE_STRING, offsetof(TableAttribute, type) },
156   { "comment", PROP_TYPE_STRING, offsetof(TableAttribute, comment) },
157   { "primary_key", PROP_TYPE_BOOL, offsetof(TableAttribute, primary_key) },
158   { "nullable", PROP_TYPE_BOOL, offsetof(TableAttribute, nullable) },
159   { "unique", PROP_TYPE_BOOL, offsetof(TableAttribute, unique) },
160 
161   { NULL, 0, 0 },
162 };
163 
164 static PropDescDArrayExtra table_attribute_extra =
165   {
166     { table_attribute_props, table_attribute_offsets, "table_attribute" },
167     (NewRecordFunc) table_attribute_new,
168     (FreeRecordFunc) table_attribute_free
169   };
170 
171 static PropDescription table_props[] =
172   {
173     ELEMENT_COMMON_PROPERTIES,
174 
175     PROP_STD_TEXT_COLOUR_OPTIONS(PROP_FLAG_VISIBLE | PROP_FLAG_STANDARD | PROP_FLAG_OPTIONAL),
176     PROP_STD_LINE_COLOUR_OPTIONAL,
177     PROP_STD_FILL_COLOUR_OPTIONAL,
178     PROP_STD_LINE_WIDTH_OPTIONAL,
179 
180     { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
181       N_("Name"), NULL, NULL },
182     { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
183       N_("Comment"), NULL, NULL },
184     { "visible_comment", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
185       N_("Visible comments"), NULL, NULL },
186     { "tagging_comment", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
187       N_("Comment tagging"), NULL, NULL },
188     { "underline_primary_key", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
189       N_("Underline primary keys"), NULL, NULL },
190     { "bold_primary_keys", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
191       N_("Use bold font for primary keys"), NULL, NULL },
192 
193     PROP_MULTICOL_BEGIN("table"),
194     PROP_MULTICOL_COLUMN("font"),
195     { "normal_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
196       N_("Normal"), NULL, NULL },
197     { "name_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
198       N_("Tablename"), NULL, NULL },
199     { "comment_font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
200       N_("Comment"), NULL, NULL },
201     PROP_MULTICOL_COLUMN("height"),
202     { "normal_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
203       N_(" "), NULL, NULL },
204     { "name_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
205       N_(" "), NULL, NULL },
206     { "comment_font_height", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
207       N_(" "), NULL, NULL },
208     PROP_MULTICOL_END("table"),
209 
210     { "attributes", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL | PROP_FLAG_DONT_MERGE | PROP_FLAG_NO_DEFAULTS,
211       N_("Attributes"), NULL, &table_attribute_extra },
212 
213     PROP_DESC_END
214   };
215 
216 static PropOffset table_offsets[] = {
217   ELEMENT_COMMON_PROPERTIES_OFFSETS,
218 
219   { "text_colour", PROP_TYPE_COLOUR, offsetof(Table, text_color) },
220   { "line_colour", PROP_TYPE_COLOUR, offsetof(Table, line_color) },
221   { "fill_colour", PROP_TYPE_COLOUR, offsetof(Table, fill_color) },
222   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Table, border_width) },
223   { "name", PROP_TYPE_STRING, offsetof(Table, name) },
224   { "comment", PROP_TYPE_STRING, offsetof(Table, comment) },
225   { "visible_comment", PROP_TYPE_BOOL, offsetof(Table, visible_comment) },
226   { "tagging_comment", PROP_TYPE_BOOL, offsetof(Table, tagging_comment) },
227   { "underline_primary_key", PROP_TYPE_BOOL, offsetof(Table, underline_primary_key) },
228   { "bold_primary_keys", PROP_TYPE_BOOL, offsetof(Table, bold_primary_key) },
229 
230   PROP_OFFSET_MULTICOL_BEGIN("table"),
231   PROP_OFFSET_MULTICOL_COLUMN("font"),
232   { "normal_font", PROP_TYPE_FONT, offsetof(Table, normal_font) },
233   { "name_font", PROP_TYPE_FONT, offsetof(Table, name_font) },
234   { "comment_font", PROP_TYPE_FONT, offsetof(Table, comment_font) },
235 
236   PROP_OFFSET_MULTICOL_COLUMN("height"),
237   { "normal_font_height", PROP_TYPE_REAL, offsetof(Table, normal_font_height) },
238   { "name_font_height", PROP_TYPE_REAL, offsetof(Table, name_font_height) },
239   { "comment_font_height", PROP_TYPE_REAL, offsetof(Table, comment_font_height) },
240   PROP_OFFSET_MULTICOL_END("table"),
241 
242   { "attributes", PROP_TYPE_DARRAY, offsetof(Table, attributes) },
243 
244   { NULL, 0, 0 }
245 };
246 
247 static DiaMenuItem table_menu_items[] = {
248   { N_("Show comments"), table_show_comments_cb, NULL,
249     DIAMENU_ACTIVE | DIAMENU_TOGGLE },
250 };
251 
252 static DiaMenu table_menu = {
253   N_("Table"),
254   sizeof (table_menu_items)/sizeof (DiaMenuItem),
255   table_menu_items,
256   NULL
257 };
258 
259 /* ----------------------------------------------------------------------- */
260 
261 /**
262  * Create a new TableAttribute
263  */
table_attribute_new(void)264 TableAttribute * table_attribute_new (void)
265 {
266   TableAttribute * attr;
267 
268   attr = g_new0 (TableAttribute, 1);
269   if (attr != NULL)
270     {
271       attr->name = g_strdup ("");
272       attr->type = g_strdup ("");
273       attr->comment = g_strdup ("");
274       attr->primary_key = FALSE;
275       /* by default nullable */
276       attr->nullable = TRUE;
277       /* by default not unique */
278       attr->unique = FALSE;
279 
280       attr->left_connection = NULL;
281       attr->right_connection = NULL;
282     }
283   return attr;
284 }
285 
286 void
table_attribute_ensure_connection_points(TableAttribute * attr,DiaObject * obj)287 table_attribute_ensure_connection_points (TableAttribute * attr,
288                                           DiaObject * obj)
289 {
290   if (attr->left_connection == NULL)
291     attr->left_connection = g_new0 (ConnectionPoint, 1);
292   g_assert (attr->left_connection != NULL);
293   attr->left_connection->object = obj;
294   if (attr->right_connection == NULL)
295     attr->right_connection = g_new0 (ConnectionPoint, 1);
296   g_assert (attr->right_connection != NULL);
297   attr->right_connection->object = obj;
298 }
299 
300 /**
301  * Free a TableAttribute and its allocated resources. Upon return of
302  * this function the passed pointer will not be valid anymore.
303  */
table_attribute_free(TableAttribute * attr)304 void table_attribute_free (TableAttribute * attr)
305 {
306   if (attr->name) g_free (attr->name);
307   if (attr->type) g_free (attr->type);
308   if (attr->comment) g_free (attr->comment);
309 
310   /* do not free the connection points here as they may be shared */
311 
312   g_free (attr);
313 }
314 
315 /**
316  * Create a copy of the passed attribute. The returned copy of the
317  * attribute needs to be freed using g_free when it is no longer needed.
318  */
319 TableAttribute *
table_attribute_copy(TableAttribute * orig)320 table_attribute_copy (TableAttribute * orig)
321 {
322   TableAttribute * copy;
323 
324   copy = g_new0 (TableAttribute, 1);
325   copy->name = g_strdup (orig->name);
326   copy->type = g_strdup (orig->type);
327   copy->comment = g_strdup (orig->comment);
328   copy->primary_key = orig->primary_key;
329   copy->nullable = orig->nullable;
330   copy->unique = orig->unique;
331 
332   return copy;
333 }
334 
335 /* ----------------------------------------------------------------------- */
336 
337 /**
338  * Construct and initialize a new table object.
339  */
340 static DiaObject *
table_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)341 table_create (Point * startpoint,
342                 void * user_data,
343                 Handle **handle1,
344                 Handle **handle2)
345 {
346   Table *table;
347   Element *elem;
348   DiaObject *obj;
349   gint i;
350 
351   table = g_malloc0 (sizeof (Table));
352   elem = &table->element;
353   obj = &elem->object;
354 
355   /* init data */
356   table->name = g_strdup (_("Table"));
357   table->comment = NULL;
358   table->visible_comment = FALSE;
359   table->tagging_comment = FALSE;
360   table->underline_primary_key = TRUE;
361   table->bold_primary_key = FALSE;
362   table->attributes = NULL;
363 
364   /* init property dialog pointer */
365   table->prop_dialog = NULL;
366 
367   /* init colors */
368   table->text_color = attributes_get_foreground ();
369   table->line_color = attributes_get_foreground ();
370   table->fill_color = attributes_get_background ();
371 
372   table->border_width = attributes_get_default_linewidth ();
373 
374   /* init the fonts */
375   table_init_fonts (table);
376 
377   /* now init inherited `element' */
378   elem->corner = *startpoint;
379   element_init (elem, 8, TABLE_CONNECTIONPOINTS);
380 
381   /* init the inherited `object' */
382   obj->type = &table_type;
383   obj->ops = &table_ops;
384 
385   /* init the connection points */
386   for (i = 0; i < TABLE_CONNECTIONPOINTS; i++) {
387     obj->connections[i] = &(table->connections[i]);
388     table->connections[i].object = obj;
389     table->connections[i].connected = NULL;
390   }
391 
392   /* init the 8 handles placed around the table object */
393   for (i = 0; i < 8; i++) {
394     obj->handles[i]->type = HANDLE_NON_MOVABLE;
395   }
396 
397   *handle1 = NULL;
398   *handle2 = NULL;
399 
400   table_update_primary_key_font (table);
401   table_compute_width_height (table);
402   table_update_positions (table);
403 
404   return obj;
405 }
406 
407 static DiaObject *
table_load(ObjectNode obj_node,int version,const char * filename)408 table_load (ObjectNode obj_node, int version, const char *filename)
409 {
410   Table * table;
411   Element * elem;
412   DiaObject * obj;
413   gint i;
414 
415   table = g_new0 (Table, 1);
416   elem = &table->element;
417   obj = &elem->object;
418 
419   obj->type = &table_type;
420   obj->ops = &table_ops;
421 
422   element_load (elem, obj_node);
423   element_init (elem, 8, TABLE_CONNECTIONPOINTS);
424 
425   object_load_props(obj,obj_node);
426 
427   /* fill in defaults if not given in the loaded file */
428   if (object_find_attribute (obj_node, "line_colour") == NULL)
429     table->line_color = attributes_get_foreground ();
430   if (object_find_attribute (obj_node, "text_colour") == NULL)
431     table->text_color = attributes_get_foreground ();
432   if (object_find_attribute (obj_node, "fill_colour") == NULL)
433     table->fill_color = attributes_get_background ();
434   if (object_find_attribute (obj_node, PROP_STDNAME_LINE_WIDTH) == NULL)
435     table->border_width = attributes_get_default_linewidth ();
436   if (object_find_attribute (obj_node, "underline_primary_key") == NULL)
437     table->underline_primary_key = TRUE;
438 
439   /* init the fonts */
440   table_init_fonts (table);
441 
442   /* init the connection points */
443   for (i = 0; i < TABLE_CONNECTIONPOINTS; i++) {
444     obj->connections[i] = &(table->connections[i]);
445     table->connections[i].object = obj;
446     table->connections[i].connected = NULL;
447   }
448 
449   /* init the 8 handles placed around the table object */
450   for (i = 0; i < 8; i++) {
451     obj->handles[i]->type = HANDLE_NON_MOVABLE;
452   }
453 
454   table_update_primary_key_font (table);
455   table_compute_width_height (table);
456   table_update_positions (table);
457 
458   return &table->element.object;
459 }
460 
461 static void
table_save(Table * table,ObjectNode obj_node,const char * filename)462 table_save (Table *table, ObjectNode obj_node, const char *filename)
463 {
464   object_save_props (&table->element.object, obj_node);
465 }
466 
467 static void
table_destroy(Table * table)468 table_destroy (Table * table)
469 {
470   GList * list;
471 
472   table->destroyed = TRUE;
473 
474   element_destroy (&table->element);
475 
476   g_free (table->name);
477   g_free (table->comment);
478 
479   list = table->attributes;
480   while (list != NULL)
481     {
482       TableAttribute * attr = (TableAttribute *) list->data;
483       table_attribute_free (attr);
484 
485       list = g_list_next (list);
486     }
487   g_list_free (table->attributes);
488 
489   dia_font_unref (table->normal_font);
490   dia_font_unref (table->primary_key_font);
491   dia_font_unref (table->name_font);
492   dia_font_unref (table->comment_font);
493 
494   if (table->prop_dialog != NULL) {
495     table_dialog_free (table->prop_dialog);
496   }
497 }
498 
499 static void
table_draw(Table * table,DiaRenderer * renderer)500 table_draw (Table *table, DiaRenderer *renderer)
501 {
502   DiaRendererClass * renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
503   real y = 0.0;
504   Element * elem;
505 
506   renderer_ops->set_linewidth (renderer, table->border_width);
507   renderer_ops->set_fillstyle (renderer, FILLSTYLE_SOLID);
508   renderer_ops->set_linestyle (renderer, LINESTYLE_SOLID);
509 
510   elem = &table->element;
511 
512   y = table_draw_namebox (table, renderer, elem);
513   y = table_draw_attributesbox (table, renderer, elem, y);
514 }
515 
516 static real
table_draw_namebox(Table * table,DiaRenderer * renderer,Element * elem)517 table_draw_namebox (Table * table, DiaRenderer * renderer, Element * elem)
518 {
519   DiaRendererClass * renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
520   Point startP;
521   Point endP;
522 
523   /* upper left corner */
524   startP.x = elem->corner.x;
525   startP.y = elem->corner.y;
526   /* lower right corner */
527   endP.x = startP.x + elem->width;
528   endP.y = startP.y + table->namebox_height;
529 
530   /* first draw the outer box and fill for the class name object */
531   renderer_ops->fill_rect (renderer, &startP, &endP, &table->fill_color);
532   renderer_ops->draw_rect (renderer, &startP, &endP, &table->line_color);
533 
534   if (IS_NOT_EMPTY(table->name))
535     {
536       startP.x += elem->width / 2.0;
537       startP.y += table->name_font_height;
538       renderer_ops->set_font (renderer,
539                               table->name_font,
540                               table->name_font_height);
541       renderer_ops->draw_string (renderer,
542                                  table->name,
543                                  &startP,
544                                  ALIGN_CENTER,
545                                  &table->text_color);
546     }
547 
548   if (table->visible_comment && IS_NOT_EMPTY(table->comment))
549     {
550       draw_comments (renderer, table->comment_font, table->comment_font_height,
551                      &table->text_color, table->comment, table->tagging_comment,
552                      TABLE_COMMENT_MAXWIDTH, &startP, ALIGN_CENTER);
553     }
554 
555   return endP.y;
556 }
557 
558 
559 /**
560  *
561  * pn: This function is "borrowed" from the source code for the UML
562  * class object.
563  *
564  * Draw the comment at the point, p, using the comment font from the
565  * class defined by umlclass. When complete update the point to reflect
566  * the size of data drawn.
567  * The comment will have been word wrapped using the function
568  * create_documenattion_tag, so it may have more than one line on the
569  * display.
570  *
571  * @param   renderer            The Renderer on which the comment is being drawn
572  * @param   font                The font to render the comment in.
573  * @param   font_height         The Y size of the font used to render the comment
574  * @param   text_color          A pointer to the color to use to render the comment
575  * @param   comment             The comment string to render
576  * @param   comment_tagging     If the {documentation = } tag should be enforced
577  * @param   Comment_line_length The maximum length of any one line in the comment
578  * @param   p                   The point at which the comment is to start
579  * @param   alignment           The method to use for alignment of the font
580  * @see   uml_create_documentation
581  */
582 static void
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)583 draw_comments(DiaRenderer *renderer,
584               DiaFont     *font,
585               real         font_height,
586               Color       *text_color,
587               gchar       *comment,
588               gboolean     comment_tagging,
589               gint         Comment_line_length,
590               Point       *p,
591               gint         alignment)
592 {
593   gint      NumberOfLines = 0;
594   gint      Index;
595   gchar     *CommentString = 0;
596   gchar     *NewLineP= NULL;
597   gchar     *RenderP;
598 
599   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
600 
601   CommentString =
602     create_documentation_tag(comment, comment_tagging, Comment_line_length, &NumberOfLines);
603   RenderP = CommentString;
604   renderer_ops->set_font(renderer, font, font_height);
605   for ( Index=0; Index < NumberOfLines; Index++)
606   {
607     p->y += font_height;                    /* Advance to the next line */
608     NewLineP = strchr(RenderP, '\n');
609     if ( NewLineP != NULL)
610     {
611       *NewLineP++ = '\0';
612     }
613     renderer_ops->draw_string(renderer, RenderP, p, alignment, text_color);
614     RenderP = NewLineP;
615     if ( NewLineP == NULL){
616         break;
617     }
618   }
619   g_free(CommentString);
620 }
621 
622 static void
fill_diamond(DiaRenderer * renderer,real half_height,real width,Point * lower_midpoint,Color * color)623 fill_diamond (DiaRenderer *renderer, real half_height, real width,
624               Point * lower_midpoint, Color * color)
625 {
626   Point poly[4];
627 
628   poly[0].x = lower_midpoint->x - width/2.0;
629   poly[0].y = lower_midpoint->y;
630   poly[1].x = lower_midpoint->x;
631   poly[1].y = lower_midpoint->y + half_height;
632   poly[2].x = lower_midpoint->x + width/2.0;
633   poly[2].y = lower_midpoint->y;
634   poly[3].x = poly[1].x;
635   poly[3].y = lower_midpoint->y - half_height;
636 
637   DIA_RENDERER_GET_CLASS (renderer)->set_fillstyle (renderer, FILLSTYLE_SOLID);
638   DIA_RENDERER_GET_CLASS (renderer)->set_linejoin (renderer, LINEJOIN_MITER);
639 
640   DIA_RENDERER_GET_CLASS (renderer)->fill_polygon (renderer, poly, 4, color);
641 }
642 
643 static real
table_draw_attributesbox(Table * table,DiaRenderer * renderer,Element * elem,real Yoffset)644 table_draw_attributesbox (Table * table, DiaRenderer * renderer,
645                           Element * elem, real Yoffset)
646 {
647   DiaRendererClass * renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
648   Point startP, startTypeP;
649   Point endP;
650   Point indicP;
651   GList * list;
652   Color * text_color = &table->text_color;
653   Color * fill_color = &table->fill_color;
654   Color * line_color = &table->line_color;
655   DiaFont * attr_font;
656   real attr_font_height;
657 
658   startP.x = elem->corner.x;
659   startP.y = Yoffset;
660 
661   endP.x = startP.x + elem->width;
662   endP.y = startP.y + table->attributesbox_height;
663 
664   renderer_ops->fill_rect (renderer, &startP, &endP, fill_color);
665   renderer_ops->draw_rect (renderer, &startP, &endP, line_color);
666 
667   startP.x += TABLE_ATTR_NAME_OFFSET;
668   startP.x += (table->border_width/2.0 + 0.1);
669 
670   list = table->attributes;
671   while (list != NULL)
672     {
673       TableAttribute * attr = (TableAttribute *) list->data;
674 
675       if (attr->primary_key)
676         {
677           attr_font = table->primary_key_font;
678           attr_font_height = table->primary_key_font_height;
679         }
680       else
681         {
682           attr_font = table->normal_font;
683           attr_font_height = table->normal_font_height;
684         }
685 
686       startP.y += attr_font_height;
687       renderer_ops->set_font (renderer,
688                               attr_font,
689                               attr_font_height);
690 
691       renderer_ops->set_linewidth (renderer, TABLE_ATTR_INDIC_LINE_WIDTH);
692       indicP = startP;
693       indicP.x -= ((TABLE_ATTR_NAME_OFFSET/2.0)
694                    + (TABLE_ATTR_INDIC_WIDTH/4.0));
695       indicP.y -= (attr_font_height/2.0);
696       indicP.y += TABLE_ATTR_INDIC_WIDTH/2.0;
697       if (attr->primary_key)
698         {
699           fill_diamond (renderer,
700                         0.75*TABLE_ATTR_INDIC_WIDTH,
701                         TABLE_ATTR_INDIC_WIDTH,
702                         &indicP,
703                         &table->line_color);
704         }
705       else if (attr->nullable)
706         {
707           renderer_ops->draw_ellipse (renderer,
708                                       &indicP,
709                                       TABLE_ATTR_INDIC_WIDTH,
710                                       TABLE_ATTR_INDIC_WIDTH,
711                                       &table->line_color);
712         }
713       else
714         {
715           renderer_ops->fill_ellipse (renderer,
716                                       &indicP,
717                                       TABLE_ATTR_INDIC_WIDTH,
718                                       TABLE_ATTR_INDIC_WIDTH,
719                                       &table->line_color);
720         }
721 
722       if (IS_NOT_EMPTY(attr->name))
723         {
724           renderer_ops->draw_string (renderer,
725                                      attr->name,
726                                      &startP,
727                                      ALIGN_LEFT,
728                                      text_color);
729         }
730       if (IS_NOT_EMPTY(attr->type))
731         {
732           startTypeP = startP;
733           startTypeP.x += table->maxwidth_attr_name + TABLE_ATTR_NAME_TYPE_GAP;
734           renderer_ops->draw_string (renderer,
735                                      attr->type,
736                                      &startTypeP,
737                                      ALIGN_LEFT,
738                                      text_color);
739         }
740 
741       if (table->underline_primary_key && attr->primary_key)
742         underline_table_attribute (renderer, startP, attr, table);
743 
744       if (table->visible_comment && IS_NOT_EMPTY(attr->comment))
745         {
746           startP.x += TABLE_ATTR_COMMENT_OFFSET;
747           draw_comments (renderer,
748                          table->comment_font,
749                          table->comment_font_height,
750                          text_color,
751                          attr->comment,
752                          table->tagging_comment,
753                          TABLE_COMMENT_MAXWIDTH,
754                          &startP,
755                          ALIGN_LEFT);
756           startP.x -= TABLE_ATTR_COMMENT_OFFSET;
757           startP.y += table->comment_font_height/2;
758         }
759 
760       list = g_list_next (list);
761     }
762 
763   return Yoffset;
764 }
765 
766 static real
table_distance_from(Table * table,Point * point)767 table_distance_from (Table * table, Point *point)
768 {
769   const Rectangle * rect;
770   DiaObject * obj;
771 
772   obj = &table->element.object;
773   rect = dia_object_get_bounding_box (obj);
774   return distance_rectangle_point (rect, point);
775 }
776 
777 static void
table_select(Table * table,Point * clicked_point,DiaRenderer * interactive_renderer)778 table_select (Table * table, Point * clicked_point,
779                 DiaRenderer * interactive_renderer)
780 {
781   element_update_handles (&table->element);
782 }
783 
784 static DiaObject *
table_copy(Table * orig)785 table_copy(Table * orig)
786 {
787   Table * copy;
788   Element * orig_elem, * copy_elem;
789   DiaObject * copy_obj;
790   GList * list;
791   gint i;
792 
793   orig_elem = &orig->element;
794 
795   copy = g_new0 (Table, 1);
796   copy_elem = &copy->element;
797   copy_obj = &copy_elem->object;
798   element_copy (orig_elem, copy_elem);
799 
800   for (i = 0; i < TABLE_CONNECTIONPOINTS; i++)
801     {
802       copy_obj->connections[i] = &copy->connections[i];
803       copy->connections[i].object = copy_obj;
804       copy->connections[i].connected = NULL;
805       copy->connections[i].pos = orig->connections[i].pos;
806       copy->connections[i].last_pos = orig->connections[i].last_pos;
807     }
808 
809   copy->name = g_strdup (orig->name);
810   copy->comment = g_strdup (orig->comment);
811   copy->visible_comment = orig->visible_comment;
812   copy->tagging_comment = orig->tagging_comment;
813   copy->underline_primary_key = orig->underline_primary_key;
814   copy->bold_primary_key = orig->bold_primary_key;
815   list = orig->attributes;
816   i = TABLE_CONNECTIONPOINTS;
817   while (list != NULL)
818     {
819       TableAttribute * orig_attr = (TableAttribute *) list->data;
820       TableAttribute * copy_attr = table_attribute_copy (orig_attr);
821       table_attribute_ensure_connection_points (copy_attr, copy_obj);
822       copy_obj->connections[i++] = copy_attr->left_connection;
823       copy_obj->connections[i++] = copy_attr->right_connection;
824 
825       copy->attributes = g_list_append (copy->attributes, copy_attr);
826       list = g_list_next (list);
827     }
828   copy->normal_font_height = orig->normal_font_height;
829   copy->normal_font = dia_font_ref (orig->normal_font);
830   copy->name_font_height = orig->name_font_height;
831   copy->name_font = dia_font_ref (orig->name_font);
832   copy->comment_font_height = orig->comment_font_height;
833   copy->comment_font = dia_font_ref (orig->comment_font);
834   copy->text_color = orig->text_color;
835   copy->line_color = orig->line_color;
836   copy->fill_color = orig->fill_color;
837   copy->border_width = orig->border_width;
838 
839   table_update_primary_key_font (copy);
840   table_compute_width_height (copy);
841   table_update_positions (copy);
842 
843   return &copy->element.object;
844 }
845 
846 static ObjectChange *
table_move(Table * table,Point * to)847 table_move (Table *table, Point *to)
848 {
849   table->element.corner = *to;
850   table_update_positions (table);
851   return NULL;
852 }
853 
854 static ObjectChange *
table_move_handle(Table * table,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)855 table_move_handle (Table *table, Handle *handle,
856                    Point *to, ConnectionPoint *cp,
857                    HandleMoveReason reason, ModifierKeys modifiers)
858 {
859   /* ignore this event */
860   return NULL;
861 }
862 
863 static PropDescription *
table_describe_props(Table * table)864 table_describe_props (Table *table)
865 {
866   if (table_props[0].quark == 0) {
867     prop_desc_list_calculate_quarks (table_props);
868   }
869 
870   return table_props;
871 }
872 
873 static void
table_get_props(Table * table,GPtrArray * props)874 table_get_props (Table * table, GPtrArray *props)
875 {
876   object_get_props_from_offsets (&table->element.object, table_offsets, props);
877 }
878 
879 static void
table_set_props(Table * table,GPtrArray * props)880 table_set_props (Table *table, GPtrArray *props)
881 {
882   object_set_props_from_offsets (&table->element.object, table_offsets, props);
883 
884   if (find_prop_by_name (props, "normal_font_height") != NULL)
885     table->primary_key_font_height = table->normal_font_height;
886 
887   if (find_prop_by_name (props, "normal_font") != NULL)
888     table_update_primary_key_font (table);
889 
890   /* the following routines depend on the fonts, and we can get called
891    * here during load and the fonts are optional ...
892    */
893   if (table->normal_font && table->name_font && table->comment_font)
894     {
895       table_update_connectionpoints (table);
896       table_compute_width_height (table);
897       table_update_positions (table);
898     }
899 }
900 
901 ObjectChange *
_table_dialog_apply_changes(Table * table,GtkWidget * widget)902 _table_dialog_apply_changes (Table * table, GtkWidget * widget)
903 {
904   /* fallback, if it isn't our dialog, e.g. during multiple selection change */
905   if (!table->prop_dialog)
906     return object_apply_props_from_dialog(&table->element.object, widget);
907   else
908     return table_dialog_apply_changes(table, widget);
909 }
910 
911 /**
912  * Init the height and width of the underlying element. This routine
913  * uses `table_calculate_namebox_data' and
914  * `table_init_attributesbox_height' which require the table's font
915  * members to be initialized, so be sure to call this routine after the
916  * font members were already initialized.
917  */
918 void
table_compute_width_height(Table * table)919 table_compute_width_height (Table * table)
920 {
921   real width = 0.0;
922   real maxwidth = 0.0;
923 
924   width = table_calculate_namebox_data (table);
925   table->element.height = table->namebox_height;
926   maxwidth = MAX(width, maxwidth);
927 
928   width = table_init_attributesbox_height (table);
929   table->element.height += table->attributesbox_height;
930   maxwidth = MAX(width, maxwidth);
931 
932   table->element.width = maxwidth + 0.5;
933 }
934 
935 /**
936  * Compute the height of the box surrounding the table's attributes,
937  * store it in the passed table structure and return the width into
938  * which all attributes of the table fit. While traversing the table's
939  * attributes to compute the height, store also the width of the longest
940  * attribute name in the passed table structure. This function makes use
941  * of the fonts defined in the passed table structure, so be sure to
942  * initialize them before calling this routine.
943  */
944 static real
table_init_attributesbox_height(Table * table)945 table_init_attributesbox_height (Table * table)
946 {
947   real maxwidth_name = 0.0;
948   real maxwidth_type = 0.0;
949   real maxwidth_comment = 0.0;
950   real width = 0.0;
951   GList * list = table->attributes;
952   DiaFont * comment_font = table->comment_font;
953   real comment_font_height = table->comment_font_height;
954   DiaFont * attr_font;
955   real attr_font_height;
956 
957   /* height of an empty attributesbox */
958   table->attributesbox_height = 2*0.1;
959 
960   while (list != NULL) {
961     TableAttribute * attrib = (TableAttribute *) list->data;
962 
963     if (attrib->primary_key)
964       {
965         attr_font = table->primary_key_font;
966         attr_font_height = table->primary_key_font_height;
967       }
968     else
969       {
970         attr_font = table->normal_font;
971         attr_font_height = table->normal_font_height;
972       }
973 
974     if (IS_NOT_EMPTY(attrib->name))
975       {
976         width = dia_font_string_width (attrib->name,
977                                        attr_font, attr_font_height);
978         maxwidth_name = MAX(maxwidth_name, width);
979       }
980     if (IS_NOT_EMPTY(attrib->type))
981       {
982         width = dia_font_string_width (attrib->type,
983                                        attr_font, attr_font_height);
984         maxwidth_type = MAX(maxwidth_type, width);
985       }
986     table->attributesbox_height += attr_font_height;
987 
988     if (table->visible_comment && IS_NOT_EMPTY(attrib->comment))
989       {
990         int num_of_lines = 0;
991         gchar * cmt_str = create_documentation_tag (attrib->comment,
992                                                     table->tagging_comment,
993                                                     TABLE_COMMENT_MAXWIDTH,
994                                                     &num_of_lines);
995         width = dia_font_string_width (cmt_str,
996                                        comment_font,
997                                        comment_font_height);
998         width += TABLE_ATTR_COMMENT_OFFSET;
999         g_free (cmt_str);
1000 
1001         table->attributesbox_height += (comment_font_height * num_of_lines);
1002         table->attributesbox_height += comment_font_height / 2;
1003         maxwidth_comment = MAX(maxwidth_comment, width);
1004       }
1005 
1006     list = g_list_next (list);
1007   }
1008   table->maxwidth_attr_name = maxwidth_name;
1009   width = TABLE_ATTR_NAME_OFFSET
1010     + maxwidth_name
1011     + maxwidth_type
1012     + TABLE_ATTR_NAME_TYPE_GAP;
1013   return MAX(width, maxwidth_comment);
1014 }
1015 
1016 static void
underline_table_attribute(DiaRenderer * renderer,Point StartPoint,TableAttribute * attr,Table * table)1017 underline_table_attribute(DiaRenderer  *   renderer,
1018                           Point            StartPoint,
1019                           TableAttribute * attr,
1020                           Table *          table)
1021 {
1022   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
1023   Point    UnderlineStartPoint;
1024   Point    UnderlineEndPoint;
1025   DiaFont * font;
1026   real font_height;
1027 
1028   if (attr->primary_key)
1029     {
1030       font = table->primary_key_font;
1031       font_height = table->primary_key_font_height;
1032     }
1033   else
1034     {
1035       font = table->normal_font;
1036       font_height = table->normal_font_height;
1037     }
1038 
1039   UnderlineStartPoint = StartPoint;
1040   UnderlineStartPoint.y += font_height * 0.1;
1041   UnderlineEndPoint = UnderlineStartPoint;
1042   UnderlineEndPoint.x += table->maxwidth_attr_name + TABLE_ATTR_NAME_TYPE_GAP;
1043   if (IS_NOT_EMPTY(attr->type))
1044     {
1045       UnderlineEndPoint.x += dia_font_string_width(attr->type,
1046                                                    font,
1047                                                    font_height);
1048     }
1049   renderer_ops->set_linewidth(renderer, TABLE_UNDERLINE_WIDTH);
1050   renderer_ops->draw_line(renderer,
1051                           &UnderlineStartPoint,
1052                           &UnderlineEndPoint,
1053                           &table->text_color);
1054 }
1055 
1056 /**
1057  * pn: This function is "borrowed" from the source code for the UML
1058  * class object.
1059  *
1060  * Create a documentation tag from a comment.
1061  *
1062  * First a string is created containing only the text
1063  * "{documentation = ". Then the contents of the comment string
1064  * are added but wrapped. This is done by first looking for any
1065  * New Line characters. If the line segment is longer than the
1066  * WrapPoint would allow, the line is broken at either the
1067  * first whitespace before the WrapPoint or if there are no
1068  * whitespaces in the segment, at the WrapPoint.  This
1069  * continues until the entire string has been processed and
1070  * then the resulting new string is returned. No attempt is
1071  * made to rejoin any of the segments, that is all New Lines
1072  * are treated as hard newlines. No syllable matching is done
1073  * either so breaks in words will sometimes not make real
1074  * sense.
1075  * <p>
1076  * Finally, since this function returns newly created dynamic
1077  * memory the caller must free the memory to prevent memory
1078  * leaks.
1079  *
1080  * @param  comment       The comment to be wrapped to the line length limit
1081  * @param  WrapPoint     The maximum line length allowed for the line.
1082  * @param  NumberOfLines The number of comment lines after the wrapping.
1083  * @return               a pointer to the wrapped documentation
1084  *
1085  *  NOTE:
1086  *      This function should most likely be move to a source file for
1087  *      handling global UML functionallity at some point.
1088  */
1089 static gchar *
create_documentation_tag(gchar * comment,gboolean tagging,gint WrapPoint,gint * NumberOfLines)1090 create_documentation_tag (gchar * comment,
1091                           gboolean tagging,
1092                           gint WrapPoint,
1093                           gint *NumberOfLines)
1094 {
1095   gchar  *CommentTag           = tagging ? "{documentation = " : "";
1096   gint   TagLength             = strlen(CommentTag);
1097   /* Make sure that there is at least some value greater then zero for the WrapPoint to
1098    * support diagrams from earlier versions of Dia. So if the WrapPoint is zero then use
1099    * the taglength as the WrapPoint. If the Tag has been changed such that it has a length
1100    * of 0 then use 1.
1101    */
1102   gint     WorkingWrapPoint = (TagLength<WrapPoint) ? WrapPoint : ((TagLength<=0)?1:TagLength);
1103   gint     RawLength        = TagLength + strlen(comment) + (tagging?1:0);
1104   gint     MaxCookedLength  = RawLength + RawLength/WorkingWrapPoint;
1105   gchar    *WrappedComment  = g_malloc0(MaxCookedLength+1);
1106   gint     AvailSpace       = WorkingWrapPoint - TagLength;
1107   gchar    *Scan;
1108   gchar    *BreakCandidate;
1109   gunichar ScanChar;
1110   gboolean AddNL            = FALSE;
1111 
1112   if (tagging)
1113     strcat(WrappedComment, CommentTag);
1114   *NumberOfLines = 1;
1115 
1116   while ( *comment ) {
1117     /* Skip spaces */
1118     while ( *comment && g_unichar_isspace(g_utf8_get_char(comment)) ) {
1119         comment = g_utf8_next_char(comment);
1120     }
1121     /* Copy chars */
1122     if ( *comment ){
1123       /* Scan to \n or avalable space exhausted */
1124       Scan = comment;
1125       BreakCandidate = NULL;
1126       while ( *Scan && *Scan != '\n' && AvailSpace > 0 ) {
1127         ScanChar = g_utf8_get_char(Scan);
1128         /* We known, that g_unichar_isspace() is not recommended for word breaking;
1129          * but Pango usage seems too complex.
1130          */
1131         if ( g_unichar_isspace(ScanChar) )
1132           BreakCandidate = Scan;
1133         AvailSpace--; /* not valid for nonspacing marks */
1134         Scan = g_utf8_next_char(Scan);
1135       }
1136       if ( AvailSpace==0 && BreakCandidate != NULL )
1137         Scan = BreakCandidate;
1138       if ( AddNL ){
1139         strcat(WrappedComment, "\n");
1140         *NumberOfLines+=1;
1141       }
1142       AddNL = TRUE;
1143       strncat(WrappedComment, comment, Scan-comment);
1144         AvailSpace = WorkingWrapPoint;
1145       comment = Scan;
1146     }
1147   }
1148   if (tagging)
1149     strcat(WrappedComment, "}");
1150   assert(strlen(WrappedComment)<=MaxCookedLength);
1151   return WrappedComment;
1152 }
1153 
1154 /**
1155  * Compute the dimension of the box surrounding the table's name and its
1156  * comment if any and if it is visible, store it (the height) in the
1157  * passed table structure and return the width of the namebox. This
1158  * function makes use of the fonts defined in the passed table
1159  * structure, so be sure to initialize them before calling this routine.
1160  */
1161 static real
table_calculate_namebox_data(Table * table)1162 table_calculate_namebox_data (Table * table)
1163 {
1164   real maxwidth = 0.0;
1165   real width = 0.0;
1166 
1167   if (IS_NOT_EMPTY(table->name))
1168     {
1169       maxwidth = width = dia_font_string_width (table->name,
1170                                                 table->name_font,
1171                                                 table->name_font_height);
1172     }
1173   table->namebox_height = table->name_font_height + 2*0.1;
1174 
1175   if (table->visible_comment && IS_NOT_EMPTY(table->comment))
1176     {
1177       gint numOfCommentLines = 0;
1178       gchar * wrapped_box = create_documentation_tag (table->comment,
1179                                                       table->tagging_comment,
1180                                                       TABLE_COMMENT_MAXWIDTH,
1181                                                       &numOfCommentLines);
1182       width = dia_font_string_width (wrapped_box,
1183                                      table->comment_font,
1184                                      table->comment_font_height);
1185       g_free (wrapped_box);
1186       table->namebox_height += table->comment_font_height * numOfCommentLines;
1187       maxwidth = MAX(width, maxwidth);
1188     }
1189 
1190   return maxwidth;
1191 }
1192 
1193 void
table_update_positions(Table * table)1194 table_update_positions (Table *table)
1195 {
1196   ConnectionPoint * connections = table->connections;
1197   Element * elem = &table->element;
1198   GList * list;
1199   coord x, y;
1200   real pointspacing;
1201   gint i;
1202   gint pointswide;
1203   gint southWestIndex;
1204   real attr_font_height;
1205 
1206   x = elem->corner.x;
1207   y = elem->corner.y;
1208 
1209   /* north-west */
1210   connpoint_update (&connections[0], x, y, DIR_NORTHWEST);
1211 
1212   /* dynamic number of connection points between north-west and north-east */
1213   pointswide = (TABLE_CONNECTIONPOINTS - 6) / 2;
1214   pointspacing = elem->width / (pointswide + 1.0);
1215 
1216   for (i = 1; i <= pointswide; i++) {
1217     connpoint_update (&connections[i], x + (pointspacing * i), y,
1218                       DIR_NORTH);
1219   }
1220 
1221   /* north-east */
1222   i = (TABLE_CONNECTIONPOINTS / 2) - 2;
1223   connpoint_update (&connections[i], x + elem->width, y,
1224                     DIR_NORTHEAST);
1225 
1226   /* west */
1227   i = (TABLE_CONNECTIONPOINTS / 2) - 1;
1228   connpoint_update (&connections[i], x, y + table->namebox_height / 2.0,
1229                     DIR_WEST);
1230 
1231   /* east */
1232   i = (TABLE_CONNECTIONPOINTS / 2);
1233   connpoint_update (&connections[i],
1234                     x + elem->width,
1235                     y + table->namebox_height / 2.0,
1236                     DIR_EAST);
1237 
1238   /* south west */
1239   southWestIndex = i = (TABLE_CONNECTIONPOINTS / 2) + 1;
1240   connpoint_update (&connections[i], x, y + elem->height,
1241                     DIR_SOUTHWEST);
1242 
1243   /* dynamic number of connection points between south-west and south-east */
1244   for (i = 1; i <= pointswide; i++) {
1245     connpoint_update (&connections[southWestIndex + i],
1246                       x + (pointspacing * i),
1247                       y + elem->height,
1248                       DIR_SOUTH);
1249   }
1250 
1251   /* south east */
1252   i = (TABLE_CONNECTIONPOINTS - 1);
1253   connpoint_update (&connections[i], x + elem->width, y + elem->height,
1254                     DIR_SOUTHEAST);
1255 
1256   y += table->namebox_height + 0.1 + table->normal_font_height/2;
1257 
1258   list = table->attributes;
1259   while (list != NULL)
1260     {
1261       TableAttribute * attr = (TableAttribute *) list->data;
1262 
1263       attr_font_height = (attr->primary_key == TRUE)
1264         ? table->primary_key_font_height
1265         : table->normal_font_height;
1266 
1267       if (attr->left_connection != NULL)
1268         connpoint_update (attr->left_connection, x, y, DIR_WEST);
1269       if (attr->right_connection != NULL)
1270         connpoint_update (attr->right_connection, x + elem->width, y, DIR_EAST);
1271 
1272       y += attr_font_height;
1273 
1274       if (table->visible_comment && IS_NOT_EMPTY(attr->comment))
1275         {
1276           gint num_of_lines = 0;
1277           gchar * str = create_documentation_tag (attr->comment,
1278                                                   table->tagging_comment,
1279                                                   TABLE_COMMENT_MAXWIDTH,
1280                                                   &num_of_lines);
1281           y += table->comment_font_height * num_of_lines;
1282           y += table->comment_font_height/2.0;
1283           g_free (str);
1284         }
1285 
1286       list = g_list_next (list);
1287     }
1288 
1289   element_update_boundingbox (elem);
1290   elem->object.position = elem->corner;
1291   element_update_handles (elem);
1292 }
1293 
1294 /**
1295  * Fix the object's connectionpoints array to be as large as neccessary to
1296  * hold the required number of connection points and (re)initialize it. This
1297  * routine reflects changes to the table->attributes list.
1298  */
1299 void
table_update_connectionpoints(Table * table)1300 table_update_connectionpoints (Table * table)
1301 {
1302   DiaObject * obj;
1303   GList * list;
1304   gint index;
1305   gint num_connections, num_attrs;
1306 
1307   obj = &table->element.object;
1308   num_attrs = g_list_length (table->attributes);
1309   num_connections = TABLE_CONNECTIONPOINTS + 2*num_attrs;
1310   if (num_connections != obj->num_connections)
1311     {
1312       obj->num_connections = num_connections;
1313       obj->connections =
1314         g_realloc (obj->connections,
1315                    num_connections * sizeof (ConnectionPoint *));
1316     }
1317   list = table->attributes;
1318   index = TABLE_CONNECTIONPOINTS;
1319   while (list != NULL)
1320     {
1321       TableAttribute * attr = (TableAttribute *) list->data;
1322       table_attribute_ensure_connection_points (attr, obj);
1323       obj->connections[index++] = attr->left_connection;
1324       obj->connections[index++] = attr->right_connection;
1325       list = g_list_next (list);
1326     }
1327 }
1328 
1329 static DiaMenu *
table_object_menu(DiaObject * obj,Point * p)1330 table_object_menu(DiaObject *obj, Point *p)
1331 {
1332   table_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
1333     (((Table *)obj)->visible_comment ? DIAMENU_TOGGLE_ON : 0);
1334 
1335   return &table_menu;
1336 }
1337 
1338 static ObjectChange *
table_show_comments_cb(DiaObject * obj,Point * pos,gpointer data)1339 table_show_comments_cb(DiaObject *obj, Point *pos, gpointer data)
1340 {
1341   TableState * state;
1342   Table * table = (Table *) obj;
1343 
1344   state = table_state_new (table);
1345   table->visible_comment = !table->visible_comment;
1346   table_compute_width_height (table);
1347   table_update_positions (table);
1348   return (ObjectChange *) table_change_new (table, state, NULL, NULL, NULL);
1349 }
1350 
1351 /**
1352  * This routine updates the font for primary keys. It depends on
1353  * `normal_font' and `bold_primary_key' properties of the passed table.
1354  * This routine should be called when at least one of these properties
1355  * have been changed.
1356  */
1357 void
table_update_primary_key_font(Table * table)1358 table_update_primary_key_font (Table * table)
1359 {
1360   if (table->primary_key_font)
1361     dia_font_unref (table->primary_key_font);
1362   if (!table->bold_primary_key
1363       || (DIA_FONT_STYLE_GET_WEIGHT (dia_font_get_style (table->normal_font))
1364           == DIA_FONT_BOLD))
1365     {
1366       table->primary_key_font = dia_font_ref (table->normal_font);
1367     }
1368   else
1369     {
1370       table->primary_key_font = dia_font_copy (table->normal_font);
1371       dia_font_set_weight (table->primary_key_font, DIA_FONT_BOLD);
1372     }
1373 
1374   table->primary_key_font_height = table->normal_font_height;
1375 }
1376 
1377 static void
table_init_fonts(Table * table)1378 table_init_fonts (Table * table)
1379 {
1380   if (table->normal_font == NULL)
1381     {
1382       table->normal_font_height = 0.8;
1383       table->normal_font = dia_font_new_from_style (DIA_FONT_MONOSPACE, 0.8);
1384     }
1385   if (table->name_font == NULL)
1386     {
1387       table->name_font_height = 0.7;
1388       table->name_font =
1389         dia_font_new_from_style (DIA_FONT_SANS | DIA_FONT_BOLD, 0.7);
1390     }
1391   if (table->comment_font == NULL)
1392     {
1393       table->comment_font_height = 0.7;
1394       table->comment_font =
1395         dia_font_new_from_style (DIA_FONT_SANS | DIA_FONT_ITALIC, 0.7);
1396     }
1397 }
1398