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 = ©->element;
797 copy_obj = ©_elem->object;
798 element_copy (orig_elem, copy_elem);
799
800 for (i = 0; i < TABLE_CONNECTIONPOINTS; i++)
801 {
802 copy_obj->connections[i] = ©->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 ©->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