2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / UML / association.c
blob9ac5e806123893df2c0b96a6acd96d6142979e35
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 * Association updates Copyright(c) 2004 David Klotzbach
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 /*--------------------------------------------------------------------------**
23 ** In the fall of 2004, I started to use the UML portion of this dia **
24 ** program in earnest. I had been using several commercial programs in my **
25 ** work and wanted something I could use for my "home" projects. Even **
26 ** though Dia was advertised has having complete support for the UML static **
27 ** structures, I found that I was used to having a design tool that was **
28 ** much closer to the language as described by the OMG and the Three **
29 ** Amigos. **
30 ** Always willing to give back to the culture that has supported me for the **
31 ** last 30+ years, I have endeavored to make the UML portion of Dia more **
32 ** compliant with the standard as is is currently described. **
33 ** My changes do not include any of the enhancements as described in **
34 ** UML 2.0. **
35 ** **
36 ** Association change Oct 31, 2004 - Dave Klotzbach **
37 ** dklotzbach@foxvalley.net **
38 ** **
39 ** Now for a description of what I have done here in Association. To begin **
40 ** with, the implementation of Association is darn near complete. However, **
41 ** as described in "The Unified Modeling Language Users Guide", the roles **
42 ** that an association may have do have "visibility" attributes, and these **
43 ** attributes apply independently to each of the roles. **
44 ** **
45 ** What is still missing from this version are the concepts of **
46 ** "Qualification", "Interface Specifier" and "Association Classes" **
47 **--------------------------------------------------------------------------*/
49 /* DO NOT USE THIS OBJECT AS A BASIS FOR A NEW OBJECT. */
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif
55 #include <assert.h>
56 #include <gtk/gtk.h>
57 #include <math.h>
58 #include <string.h>
60 #include "intl.h"
61 #include "object.h"
62 #include "orth_conn.h"
63 #include "diarenderer.h"
64 #include "attributes.h"
65 #include "arrows.h"
66 #include "uml.h"
68 #include "properties.h"
70 #include "pixmaps/association.xpm"
72 extern char visible_char[]; /* The definitions are in UML.c. Used here to avoid getting out of sync */
74 typedef struct _Association Association;
75 typedef struct _AssociationState AssociationState;
76 typedef struct _AssociationPropertiesDialog AssociationPropertiesDialog;
78 typedef enum {
79 ASSOC_NODIR,
80 ASSOC_RIGHT,
81 ASSOC_LEFT
82 } AssociationDirection;
84 typedef enum {
85 AGGREGATE_NONE,
86 AGGREGATE_NORMAL,
87 AGGREGATE_COMPOSITION
88 } AggregateType;
90 typedef struct _AssociationEnd {
91 gchar *role; /* Can be NULL */
92 gchar *multiplicity; /* Can be NULL */
93 Point text_pos;
94 real text_width;
95 real role_ascent;
96 real role_descent;
97 real multi_ascent;
98 real multi_descent;
99 Alignment text_align;
100 UMLVisibility visibility; /* This value is only relevant if role is not null */
102 int arrow;
103 AggregateType aggregate; /* Note: Can only be != NONE on ONE side! */
104 } AssociationEnd;
106 struct _AssociationState {
107 ObjectState obj_state;
109 gchar *name;
110 AssociationDirection direction;
112 struct {
113 gchar *role;
114 gchar *multiplicity;
115 UMLVisibility visibility; /* This value is only relevant if role is not null */
117 int arrow;
118 AggregateType aggregate;
119 } end[2];
123 struct _Association {
124 OrthConn orth;
126 Point text_pos;
127 Alignment text_align;
128 real text_width;
129 real ascent;
130 real descent;
132 gchar *name;
133 AssociationDirection direction;
135 AssociationEnd end[2];
137 AssociationPropertiesDialog* properties_dialog;
140 struct _AssociationPropertiesDialog {
141 GtkWidget *dialog;
143 GtkEntry *name;
144 GtkMenu *dir_menu;
145 GtkOptionMenu *dir_omenu;
147 struct {
148 GtkEntry *role;
149 GtkEntry *multiplicity;
150 GtkMenu *attr_visible;
151 GtkOptionMenu *attr_visible_button;
152 GtkToggleButton *draw_arrow;
153 GtkToggleButton *aggregate;
154 GtkToggleButton *composition;
155 } end[2];
158 #define ASSOCIATION_WIDTH 0.1
159 #define ASSOCIATION_TRIANGLESIZE 0.8
160 #define ASSOCIATION_DIAMONDLEN 1.4
161 #define ASSOCIATION_DIAMONDWIDTH 0.7
162 #define ASSOCIATION_FONTHEIGHT 0.8
163 #define ASSOCIATION_END_SPACE 0.2
164 static DiaFont *assoc_font = NULL;
166 static real association_distance_from(Association *assoc, Point *point);
167 static void association_select(Association *assoc, Point *clicked_point,
168 DiaRenderer *interactive_renderer);
169 static ObjectChange* association_move_handle(Association *assoc, Handle *handle,
170 Point *to, ConnectionPoint *cp,
171 HandleMoveReason reason, ModifierKeys modifiers);
172 static ObjectChange* association_move(Association *assoc, Point *to);
173 static void association_draw(Association *assoc, DiaRenderer *renderer);
174 static DiaObject *association_create(Point *startpoint,
175 void *user_data,
176 Handle **handle1,
177 Handle **handle2);
178 static void association_destroy(Association *assoc);
179 static DiaObject *association_copy(Association *assoc);
180 static GtkWidget *association_get_properties(Association *assoc, gboolean is_default);
181 static ObjectChange *association_apply_properties(Association *assoc);
182 static DiaMenu *association_get_object_menu(Association *assoc,
183 Point *clickedpoint);
184 static PropDescription *association_describe_props(Association *assoc);
185 static void association_get_props(Association *assoc, GPtrArray *props);
186 static void association_set_props(Association *assoc, GPtrArray *props);
188 static AssociationState *association_get_state(Association *assoc);
189 static void association_set_state(Association *assoc,
190 AssociationState *state);
192 static void association_save(Association *assoc, ObjectNode obj_node,
193 const char *filename);
194 static DiaObject *association_load(ObjectNode obj_node, int version,
195 const char *filename);
197 static void association_update_data(Association *assoc);
198 static coord get_aggregate_pos_diff(AssociationEnd *end);
200 static ObjectTypeOps association_type_ops =
202 (CreateFunc) association_create,
203 (LoadFunc) association_load,
204 (SaveFunc) association_save
207 DiaObjectType association_type =
209 "UML - Association", /* name */
210 /* Version 0 had no autorouting and so shouldn't have it set by default. */
211 1, /* version */
212 (char **) association_xpm, /* pixmap */
214 &association_type_ops, /* ops */
215 NULL, /* pixmap_file */
216 0 /* default_user_data */
219 static ObjectOps association_ops = {
220 (DestroyFunc) association_destroy,
221 (DrawFunc) association_draw,
222 (DistanceFunc) association_distance_from,
223 (SelectFunc) association_select,
224 (CopyFunc) association_copy,
225 (MoveFunc) association_move,
226 (MoveHandleFunc) association_move_handle,
227 (GetPropertiesFunc) association_get_properties,
228 (ApplyPropertiesFunc) association_apply_properties,
229 (ObjectMenuFunc) association_get_object_menu,
230 (DescribePropsFunc) association_describe_props,
231 (GetPropsFunc) association_get_props,
232 (SetPropsFunc) association_set_props
235 static PropDescription association_props[] = {
236 ORTHCONN_COMMON_PROPERTIES,
237 { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE,
238 N_("Name:"), NULL, NULL },
239 PROP_DESC_END
242 static PropDescription *
243 association_describe_props(Association *assoc)
245 if (association_props[0].quark == 0) {
246 prop_desc_list_calculate_quarks(association_props);
248 return association_props;
251 static PropOffset association_offsets[] = {
252 ORTHCONN_COMMON_PROPERTIES_OFFSETS,
253 { "name", PROP_TYPE_STRING, offsetof(Association, name) },
254 { NULL, 0, 0 }
257 static void
258 association_get_props(Association *assoc, GPtrArray *props)
260 object_get_props_from_offsets(&assoc->orth.object,
261 association_offsets,props);
264 static void
265 association_set_props(Association *assoc, GPtrArray *props)
267 object_set_props_from_offsets(&assoc->orth.object,
268 association_offsets, props);
269 association_update_data(assoc);
272 static real
273 association_distance_from(Association *assoc, Point *point)
275 OrthConn *orth = &assoc->orth;
276 return orthconn_distance_from(orth, point, ASSOCIATION_WIDTH);
279 static void
280 association_select(Association *assoc, Point *clicked_point,
281 DiaRenderer *interactive_renderer)
283 orthconn_update_data(&assoc->orth);
286 static ObjectChange*
287 association_move_handle(Association *assoc, Handle *handle,
288 Point *to, ConnectionPoint *cp,
289 HandleMoveReason reason, ModifierKeys modifiers)
291 ObjectChange *change;
292 assert(assoc!=NULL);
293 assert(handle!=NULL);
294 assert(to!=NULL);
296 change = orthconn_move_handle(&assoc->orth, handle, to, cp, reason, modifiers);
297 association_update_data(assoc);
299 return change;
302 static ObjectChange*
303 association_move(Association *assoc, Point *to)
305 ObjectChange *change;
307 change = orthconn_move(&assoc->orth, to);
308 association_update_data(assoc);
310 return change;
313 static void
314 association_draw(Association *assoc, DiaRenderer *renderer)
316 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
317 OrthConn *orth = &assoc->orth;
318 Point *points;
319 Point poly[3];
320 int n,i;
321 Point pos;
322 Arrow startarrow, endarrow;
324 points = &orth->points[0];
325 n = orth->numpoints;
327 renderer_ops->set_linewidth(renderer, ASSOCIATION_WIDTH);
328 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
329 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
330 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
332 startarrow.length = ASSOCIATION_TRIANGLESIZE;
333 startarrow.width = ASSOCIATION_TRIANGLESIZE;
334 if (assoc->end[0].arrow) {
335 startarrow.type = ARROW_LINES;
336 } else if (assoc->end[0].aggregate != AGGREGATE_NONE) {
337 startarrow.length = ASSOCIATION_DIAMONDLEN;
338 startarrow.width = ASSOCIATION_TRIANGLESIZE*0.6;
339 startarrow.type = assoc->end[0].aggregate == AGGREGATE_NORMAL ?
340 ARROW_HOLLOW_DIAMOND : ARROW_FILLED_DIAMOND;
341 } else {
342 startarrow.type = ARROW_NONE;
344 endarrow.length = ASSOCIATION_TRIANGLESIZE;
345 endarrow.width = ASSOCIATION_TRIANGLESIZE;
346 if (assoc->end[1].arrow) {
347 endarrow.type = ARROW_LINES;
348 } else if (assoc->end[1].aggregate != AGGREGATE_NONE) {
349 endarrow.length = ASSOCIATION_DIAMONDLEN;
350 endarrow.width = ASSOCIATION_TRIANGLESIZE*0.6;
351 endarrow.type = assoc->end[1].aggregate == AGGREGATE_NORMAL ?
352 ARROW_HOLLOW_DIAMOND : ARROW_FILLED_DIAMOND;
353 } else {
354 endarrow.type = ARROW_NONE;
356 renderer_ops->draw_polyline_with_arrows(renderer, points, n,
357 ASSOCIATION_WIDTH,
358 &color_black,
359 &startarrow, &endarrow);
361 /* Name: */
362 renderer_ops->set_font(renderer, assoc_font, ASSOCIATION_FONTHEIGHT);
364 if (assoc->name != NULL) {
365 pos = assoc->text_pos;
366 renderer_ops->draw_string(renderer, assoc->name,
367 &pos, assoc->text_align,
368 &color_black);
371 /* Direction: */
372 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
374 switch (assoc->direction) {
375 case ASSOC_NODIR:
376 break;
377 case ASSOC_RIGHT:
378 poly[0].x = assoc->text_pos.x + assoc->text_width + 0.1;
379 if (assoc->text_align == ALIGN_CENTER)
380 poly[0].x -= assoc->text_width/2.0;
381 poly[0].y = assoc->text_pos.y;
382 poly[1].x = poly[0].x;
383 poly[1].y = poly[0].y - ASSOCIATION_FONTHEIGHT*0.5;
384 poly[2].x = poly[0].x + ASSOCIATION_FONTHEIGHT*0.5;
385 poly[2].y = poly[0].y - ASSOCIATION_FONTHEIGHT*0.5*0.5;
386 renderer_ops->fill_polygon(renderer, poly, 3, &color_black);
387 break;
388 case ASSOC_LEFT:
389 poly[0].x = assoc->text_pos.x - 0.2;
390 if (assoc->text_align == ALIGN_CENTER)
391 poly[0].x -= assoc->text_width/2.0;
392 poly[0].y = assoc->text_pos.y;
393 poly[1].x = poly[0].x;
394 poly[1].y = poly[0].y - ASSOCIATION_FONTHEIGHT*0.5;
395 poly[2].x = poly[0].x - ASSOCIATION_FONTHEIGHT*0.5;
396 poly[2].y = poly[0].y - ASSOCIATION_FONTHEIGHT*0.5*0.5;
397 renderer_ops->fill_polygon(renderer, poly, 3, &color_black);
398 break;
402 for (i=0;i<2;i++) {
403 AssociationEnd *end = &assoc->end[i];
404 pos = end->text_pos;
406 if (end->role != NULL) {
407 gchar *role_name = g_strdup_printf ("%c%s", visible_char[(int) end->visibility], end->role);
408 renderer_ops->draw_string(renderer,
409 role_name,
410 &pos,
411 end->text_align,
412 &color_black);
413 g_free (role_name);
414 pos.y += ASSOCIATION_FONTHEIGHT;
416 if (end->multiplicity != NULL) {
417 renderer_ops->draw_string(renderer, end->multiplicity,
418 &pos, end->text_align,
419 &color_black);
424 static void
425 association_state_free(ObjectState *ostate)
427 AssociationState *state = (AssociationState *)ostate;
428 int i;
429 g_free(state->name);
431 for (i=0;i<2;i++) {
432 g_free(state->end[i].role);
433 g_free(state->end[i].multiplicity);
437 static AssociationState *
438 association_get_state(Association *assoc)
440 int i;
441 AssociationEnd *end;
443 AssociationState *state = g_new0(AssociationState, 1);
445 state->obj_state.free = association_state_free;
447 state->name = g_strdup(assoc->name);
448 state->direction = assoc->direction;
450 for (i=0;i<2;i++) {
451 end = &assoc->end[i];
452 state->end[i].role = g_strdup(end->role);
453 state->end[i].multiplicity = g_strdup(end->multiplicity);
454 state->end[i].arrow = end->arrow;
455 state->end[i].aggregate = end->aggregate;
456 state->end[i].visibility = end->visibility;
459 return state;
462 static void
463 association_set_state(Association *assoc, AssociationState *state)
465 int i;
466 AssociationEnd *end;
468 g_free(assoc->name);
469 assoc->name = state->name;
470 assoc->text_width = 0.0;
471 assoc->ascent = 0.0;
472 assoc->descent = 0.0;
473 if (assoc->name != NULL) {
474 assoc->text_width =
475 dia_font_string_width(assoc->name, assoc_font, ASSOCIATION_FONTHEIGHT);
476 assoc->ascent =
477 dia_font_ascent(assoc->name, assoc_font, ASSOCIATION_FONTHEIGHT);
478 assoc->descent =
479 dia_font_descent(assoc->name, assoc_font, ASSOCIATION_FONTHEIGHT);
482 assoc->direction = state->direction;
484 for (i=0;i<2;i++) {
485 end = &assoc->end[i];
486 g_free(end->role);
487 g_free(end->multiplicity);
488 end->role = state->end[i].role;
489 end->multiplicity = state->end[i].multiplicity;
490 end->arrow = state->end[i].arrow;
491 end->aggregate = state->end[i].aggregate;
492 end->visibility = state->end[i].visibility;
494 end->text_width = 0.0;
495 end->role_ascent = 0.0;
496 end->role_descent = 0.0;
497 end->multi_ascent = 0.0;
498 end->multi_descent = 0.0;
499 if (end->role != NULL) {
500 end->text_width =
501 dia_font_string_width(end->role, assoc_font, ASSOCIATION_FONTHEIGHT);
502 end->role_ascent =
503 dia_font_ascent(end->role, assoc_font, ASSOCIATION_FONTHEIGHT);
504 end->role_descent =
505 dia_font_ascent(end->role, assoc_font, ASSOCIATION_FONTHEIGHT);
507 if (end->multiplicity != NULL) {
508 end->text_width = MAX(end->text_width,
509 dia_font_string_width(end->multiplicity,
510 assoc_font,
511 ASSOCIATION_FONTHEIGHT) );
512 end->role_ascent = dia_font_ascent(end->multiplicity,
513 assoc_font,ASSOCIATION_FONTHEIGHT);
514 end->role_descent = dia_font_descent(end->multiplicity,
515 assoc_font,ASSOCIATION_FONTHEIGHT);
519 g_free(state);
521 association_update_data(assoc);
524 static void
525 association_update_data_end(Association *assoc, int endnum)
527 OrthConn *orth = &assoc->orth;
528 DiaObject *obj = &orth->object;
529 Point *points = orth->points;
530 Rectangle rect;
531 AssociationEnd *end;
532 Orientation dir;
533 int n = orth->numpoints - 1, fp, sp;
535 /* Find the first and second points depending on which end: */
536 if (endnum) {
537 fp = n;
538 sp = n-1;
539 dir = assoc->orth.orientation[n-1];
540 } else {
541 fp = 0;
542 sp = 1;
543 dir = assoc->orth.orientation[0];
546 /* If the points are the same, find a better candidate: */
547 if (points[fp].x == points[sp].x && points[fp].y == points[sp].y) {
548 sp += (endnum ? -1 : 1);
549 if (sp < 0)
550 sp = 0;
551 if (sp > n)
552 sp = n;
553 if (points[fp].y != points[sp].y)
554 dir = VERTICAL;
555 else
556 dir = HORIZONTAL;
559 /* Update the text-points of the ends: */
560 end = &assoc->end[endnum];
561 end->text_pos = points[fp];
562 switch (dir) {
563 case HORIZONTAL:
564 end->text_pos.y -= end->role_descent;
565 if (points[fp].x < points[sp].x) {
566 end->text_align = ALIGN_LEFT;
567 end->text_pos.x += (get_aggregate_pos_diff(end) + ASSOCIATION_END_SPACE);
568 } else {
569 end->text_align = ALIGN_RIGHT;
570 end->text_pos.x -= (get_aggregate_pos_diff(end) + ASSOCIATION_END_SPACE);
572 break;
573 case VERTICAL:
574 if (end->arrow || end->aggregate != AGGREGATE_NONE)
575 end->text_pos.x += ASSOCIATION_DIAMONDWIDTH / 2;
576 end->text_pos.x += ASSOCIATION_END_SPACE;
578 end->text_pos.y += end->role_ascent;
579 if (points[fp].y > points[sp].y) {
580 if (end->role!=NULL)
581 end->text_pos.y -= ASSOCIATION_FONTHEIGHT;
582 if (end->multiplicity!=NULL)
583 end->text_pos.y -= ASSOCIATION_FONTHEIGHT;
586 end->text_align = ALIGN_LEFT;
587 break;
589 /* Add the text recangle to the bounding box: */
590 rect.left = end->text_pos.x
591 - (end->text_align == ALIGN_LEFT ? 0 : end->text_width);
592 rect.right = rect.left + end->text_width;
593 rect.top = end->text_pos.y - end->role_ascent;
594 rect.bottom = rect.top + 2*ASSOCIATION_FONTHEIGHT;
596 rectangle_union(&obj->bounding_box, &rect);
599 static void
600 association_update_data(Association *assoc)
602 /* FIXME: The ascent and descent computation logic here is
603 fundamentally slow. */
605 OrthConn *orth = &assoc->orth;
606 DiaObject *obj = &orth->object;
607 PolyBBExtras *extra = &orth->extra_spacing;
608 int num_segm, i;
609 Point *points;
610 Rectangle rect;
611 Orientation dir;
613 orthconn_update_data(orth);
615 extra->start_trans =
616 extra->start_long = (assoc->end[0].aggregate == AGGREGATE_NONE?
617 ASSOCIATION_WIDTH/2.0:
618 (ASSOCIATION_WIDTH + ASSOCIATION_DIAMONDLEN)/2.0);
619 extra->middle_trans = ASSOCIATION_WIDTH/2.0;
620 extra->end_trans =
621 extra->end_long = (assoc->end[1].aggregate == AGGREGATE_NONE?
622 ASSOCIATION_WIDTH/2.0:
623 (ASSOCIATION_WIDTH + ASSOCIATION_DIAMONDLEN)/2.0);
625 if (assoc->end[0].arrow)
626 extra->start_trans = MAX(extra->start_trans, ASSOCIATION_TRIANGLESIZE);
627 if (assoc->end[1].arrow)
628 extra->end_trans = MAX(extra->end_trans, ASSOCIATION_TRIANGLESIZE);
630 orthconn_update_boundingbox(orth);
632 /* Calc text pos: */
633 num_segm = assoc->orth.numpoints - 1;
634 points = assoc->orth.points;
635 i = num_segm / 2;
637 if ((num_segm % 2) == 0) { /* If no middle segment, use horizontal */
638 if (assoc->orth.orientation[i]==VERTICAL)
639 i--;
641 dir = assoc->orth.orientation[i];
642 /* also adapt for degenerated segement */
643 if (VERTICAL == dir && points[i].y == points[i+1].y)
644 dir = HORIZONTAL;
645 else if (HORIZONTAL == dir && points[i].x == points[i+1].x)
646 dir = VERTICAL;
648 switch (dir) {
649 case HORIZONTAL:
650 assoc->text_align = ALIGN_CENTER;
651 assoc->text_pos.x = 0.5*(points[i].x+points[i+1].x);
652 assoc->text_pos.y = points[i].y - assoc->descent;
653 break;
654 case VERTICAL:
655 assoc->text_align = ALIGN_LEFT;
656 assoc->text_pos.x = points[i].x + 0.1;
657 assoc->text_pos.y = 0.5*(points[i].y+points[i+1].y) - assoc->descent;
658 break;
661 /* Add the text recangle to the bounding box: */
662 rect.left = assoc->text_pos.x;
663 if (assoc->text_align == ALIGN_CENTER)
664 rect.left -= assoc->text_width/2.0;
665 rect.right = rect.left + assoc->text_width;
666 rect.top = assoc->text_pos.y - assoc->ascent;
667 rect.bottom = rect.top + ASSOCIATION_FONTHEIGHT;
669 rectangle_union(&obj->bounding_box, &rect);
671 association_update_data_end(assoc, 0);
672 association_update_data_end(assoc, 1);
675 static coord get_aggregate_pos_diff(AssociationEnd *end)
677 coord width=0;
678 if(end->arrow){
679 width = ASSOCIATION_TRIANGLESIZE;
681 switch(end->aggregate){
682 case AGGREGATE_COMPOSITION:
683 case AGGREGATE_NORMAL:
684 if(width!=0) width = MAX(ASSOCIATION_TRIANGLESIZE, ASSOCIATION_DIAMONDLEN);
685 else width = ASSOCIATION_DIAMONDLEN;
686 case AGGREGATE_NONE:
687 break;
689 return width;
692 static DiaObject *
693 association_create(Point *startpoint,
694 void *user_data,
695 Handle **handle1,
696 Handle **handle2)
698 Association *assoc;
699 OrthConn *orth;
700 DiaObject *obj;
701 int i;
702 int user_d;
704 if (assoc_font == NULL) {
705 assoc_font = dia_font_new_from_style(DIA_FONT_MONOSPACE, ASSOCIATION_FONTHEIGHT);
708 assoc = g_malloc0(sizeof(Association));
709 orth = &assoc->orth;
710 obj = &orth->object;
712 obj->type = &association_type;
714 obj->ops = &association_ops;
716 orthconn_init(orth, startpoint);
718 assoc->name = NULL;
719 assoc->direction = ASSOC_NODIR;
720 for (i=0;i<2;i++) {
721 assoc->end[i].role = NULL;
722 assoc->end[i].multiplicity = NULL;
723 assoc->end[i].arrow = FALSE;
724 assoc->end[i].aggregate = AGGREGATE_NONE;
725 assoc->end[i].text_width = 0.0;
728 assoc->text_width = 0.0;
729 assoc->properties_dialog = NULL;
731 user_d = GPOINTER_TO_INT(user_data);
732 switch (user_d) {
733 case 0:
734 /* Ok already. */
735 break;
736 case 1:
737 assoc->end[1].aggregate = AGGREGATE_NORMAL;
738 break;
741 association_update_data(assoc);
743 *handle1 = orth->handles[0];
744 *handle2 = orth->handles[orth->numpoints-2];
746 return &assoc->orth.object;
749 static ObjectChange *
750 association_add_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
752 ObjectChange *change;
753 change = orthconn_add_segment((OrthConn *)obj, clicked);
754 association_update_data((Association *)obj);
755 return change;
758 static ObjectChange *
759 association_delete_segment_callback(DiaObject *obj, Point *clicked, gpointer data)
761 ObjectChange *change;
762 change = orthconn_delete_segment((OrthConn *)obj, clicked);
763 association_update_data((Association *)obj);
764 return change;
768 static DiaMenuItem object_menu_items[] = {
769 { N_("Add segment"), association_add_segment_callback, NULL, 1 },
770 { N_("Delete segment"), association_delete_segment_callback, NULL, 1 },
771 ORTHCONN_COMMON_MENUS,
774 static DiaMenu object_menu = {
775 "Association",
776 sizeof(object_menu_items)/sizeof(DiaMenuItem),
777 object_menu_items,
778 NULL
781 static DiaMenu *
782 association_get_object_menu(Association *assoc, Point *clickedpoint)
784 OrthConn *orth;
786 orth = &assoc->orth;
787 /* Set entries sensitive/selected etc here */
788 object_menu_items[0].active = orthconn_can_add_segment(orth, clickedpoint);
789 object_menu_items[1].active = orthconn_can_delete_segment(orth, clickedpoint);
790 orthconn_update_object_menu(orth, clickedpoint, &object_menu_items[2]);
791 return &object_menu;
794 static void
795 association_destroy(Association *assoc)
797 int i;
799 orthconn_destroy(&assoc->orth);
801 g_free(assoc->name);
803 for (i=0;i<2;i++) {
804 g_free(assoc->end[i].role);
805 g_free(assoc->end[i].multiplicity);
808 if (assoc->properties_dialog != NULL) {
809 gtk_widget_destroy(assoc->properties_dialog->dialog);
810 g_free(assoc->properties_dialog);
814 static DiaObject *
815 association_copy(Association *assoc)
817 Association *newassoc;
818 OrthConn *orth, *neworth;
819 DiaObject *newobj;
820 int i;
822 orth = &assoc->orth;
824 newassoc = g_malloc0(sizeof(Association));
825 neworth = &newassoc->orth;
826 newobj = &neworth->object;
828 orthconn_copy(orth, neworth);
830 newassoc->name = g_strdup(assoc->name);
831 newassoc->direction = assoc->direction;
832 for (i=0;i<2;i++) {
833 newassoc->end[i] = assoc->end[i];
834 newassoc->end[i].role =
835 (assoc->end[i].role != NULL)?g_strdup(assoc->end[i].role):NULL;
836 newassoc->end[i].multiplicity =
837 (assoc->end[i].multiplicity != NULL)?g_strdup(assoc->end[i].multiplicity):NULL;
840 newassoc->text_width = assoc->text_width;
841 newassoc->properties_dialog = NULL;
843 association_update_data(newassoc);
845 return &newassoc->orth.object;
849 static void
850 association_save(Association *assoc, ObjectNode obj_node,
851 const char *filename)
853 int i;
854 AttributeNode attr;
855 DataNode composite;
857 orthconn_save(&assoc->orth, obj_node);
859 data_add_string(new_attribute(obj_node, "name"),
860 assoc->name);
861 data_add_enum(new_attribute(obj_node, "direction"),
862 assoc->direction);
864 attr = new_attribute(obj_node, "ends");
865 for (i=0;i<2;i++) {
866 composite = data_add_composite(attr, NULL);
868 data_add_string(composite_add_attribute(composite, "role"),
869 assoc->end[i].role);
870 data_add_string(composite_add_attribute(composite, "multiplicity"),
871 assoc->end[i].multiplicity);
872 data_add_boolean(composite_add_attribute(composite, "arrow"),
873 assoc->end[i].arrow);
874 data_add_enum(composite_add_attribute(composite, "aggregate"),
875 assoc->end[i].aggregate);
876 data_add_enum(composite_add_attribute(composite, "visibility"),
877 assoc->end[i].visibility);
881 static DiaObject *
882 association_load(ObjectNode obj_node, int version, const char *filename)
884 Association *assoc;
885 AttributeNode attr;
886 DataNode composite;
887 OrthConn *orth;
888 DiaObject *obj;
889 int i;
891 if (assoc_font == NULL) {
892 assoc_font = dia_font_new_from_style(DIA_FONT_MONOSPACE,
893 ASSOCIATION_FONTHEIGHT);
896 assoc = g_new0(Association, 1);
898 orth = &assoc->orth;
899 obj = &orth->object;
901 obj->type = &association_type;
902 obj->ops = &association_ops;
904 orthconn_load(orth, obj_node);
906 assoc->name = NULL;
907 attr = object_find_attribute(obj_node, "name");
908 if (attr != NULL)
909 assoc->name = data_string(attribute_first_data(attr));
911 assoc->text_width = 0.0;
912 if (assoc->name != NULL) {
913 assoc->text_width =
914 dia_font_string_width(assoc->name, assoc_font, ASSOCIATION_FONTHEIGHT);
917 assoc->direction = ASSOC_NODIR;
918 attr = object_find_attribute(obj_node, "direction");
919 if (attr != NULL)
920 assoc->direction = data_enum(attribute_first_data(attr));
922 attr = object_find_attribute(obj_node, "ends");
923 composite = attribute_first_data(attr);
924 for (i=0;i<2;i++) {
926 assoc->end[i].role = NULL;
927 attr = composite_find_attribute(composite, "role");
928 if (attr != NULL) {
929 assoc->end[i].role = data_string(attribute_first_data(attr));
931 if ( assoc->end[i].role != NULL
932 && 0 == strcmp(assoc->end[i].role, "")) {
933 g_free(assoc->end[i].role);
934 assoc->end[i].role = NULL;
937 assoc->end[i].multiplicity = NULL;
938 attr = composite_find_attribute(composite, "multiplicity");
939 if (attr != NULL) {
940 assoc->end[i].multiplicity = data_string(attribute_first_data(attr));
942 if ( assoc->end[i].multiplicity != NULL
943 && 0 == strcmp(assoc->end[i].multiplicity, "")) {
944 g_free(assoc->end[i].multiplicity);
945 assoc->end[i].multiplicity = NULL;
948 assoc->end[i].arrow = FALSE;
949 attr = composite_find_attribute(composite, "arrow");
950 if (attr != NULL)
951 assoc->end[i].arrow = data_boolean(attribute_first_data(attr));
953 assoc->end[i].aggregate = AGGREGATE_NONE;
954 attr = composite_find_attribute(composite, "aggregate");
955 if (attr != NULL)
956 assoc->end[i].aggregate = data_enum(attribute_first_data(attr));
958 assoc->end[i].visibility = FALSE;
959 attr = composite_find_attribute(composite, "visibility");
960 if (attr != NULL)
961 assoc->end[i].visibility = data_enum( attribute_first_data(attr) );
963 assoc->end[i].text_width = 0.0;
964 if (assoc->end[i].role != NULL) {
965 assoc->end[i].text_width =
966 dia_font_string_width(assoc->end[i].role, assoc_font,
967 ASSOCIATION_FONTHEIGHT);
969 if (assoc->end[i].multiplicity != NULL) {
970 assoc->end[i].text_width =
971 MAX(assoc->end[i].text_width,
972 dia_font_string_width(assoc->end[i].multiplicity,
973 assoc_font, ASSOCIATION_FONTHEIGHT) );
975 composite = data_next(composite);
978 assoc->properties_dialog = NULL;
980 association_set_state(assoc, association_get_state(assoc));
982 return &assoc->orth.object;
985 static ObjectChange *
986 association_apply_properties(Association *assoc)
988 AssociationPropertiesDialog *prop_dialog;
989 const char *str;
990 GtkWidget *menuitem;
991 int i;
992 ObjectState *old_state;
994 prop_dialog = assoc->properties_dialog;
996 old_state = (ObjectState *)association_get_state(assoc);
998 /* Read from dialog and put in object: */
999 g_free(assoc->name);
1000 str = gtk_entry_get_text(prop_dialog->name);
1001 if (str && strlen (str) != 0)
1002 assoc->name = g_strdup (str);
1003 else
1004 assoc->name = NULL;
1006 assoc->text_width = 0.0;
1008 if (assoc->name != NULL) {
1009 assoc->text_width =
1010 dia_font_string_width(assoc->name, assoc_font, ASSOCIATION_FONTHEIGHT);
1013 menuitem = gtk_menu_get_active(prop_dialog->dir_menu);
1014 assoc->direction =
1015 GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
1017 for (i=0;i<2;i++) {
1018 AssociationEnd *end = &assoc->end[i];
1020 end->visibility = (UMLVisibility)
1021 GPOINTER_TO_INT (gtk_object_get_user_data (
1022 GTK_OBJECT (gtk_menu_get_active (prop_dialog->end[i].attr_visible))));
1024 /* Role: */
1025 g_free(end->role);
1026 str = gtk_entry_get_text(prop_dialog->end[i].role);
1027 if (str && strlen (str) != 0)
1028 end->role = g_strdup (str);
1029 else
1030 end->role = NULL;
1032 /* Multiplicity: */
1033 g_free(end->multiplicity);
1034 str = gtk_entry_get_text(prop_dialog->end[i].multiplicity);
1035 if (strlen (str) != 0)
1036 end->multiplicity = g_strdup(str);
1037 else
1038 end->multiplicity = NULL;
1040 end->text_width = 0.0;
1042 if (end->role != NULL) {
1043 end->text_width =
1044 dia_font_string_width(end->role, assoc_font, ASSOCIATION_FONTHEIGHT);
1046 if (end->multiplicity != NULL) {
1047 end->text_width =
1048 MAX(end->text_width,
1049 dia_font_string_width(end->multiplicity,
1050 assoc_font, ASSOCIATION_FONTHEIGHT) );
1053 end->arrow = prop_dialog->end[i].draw_arrow->active;
1055 end->aggregate = AGGREGATE_NONE;
1056 if (prop_dialog->end[i].aggregate->active)
1057 end->aggregate = AGGREGATE_NORMAL;
1058 if (prop_dialog->end[i].composition->active)
1059 end->aggregate = AGGREGATE_COMPOSITION;
1063 association_set_state(assoc, association_get_state(assoc));
1064 return new_object_state_change(&assoc->orth.object, old_state,
1065 (GetStateFunc)association_get_state,
1066 (SetStateFunc)association_set_state);
1069 static void
1070 fill_in_dialog(Association *assoc)
1072 AssociationPropertiesDialog *prop_dialog;
1073 int i;
1075 prop_dialog = assoc->properties_dialog;
1077 if (assoc->name != NULL)
1078 gtk_entry_set_text(prop_dialog->name, assoc->name);
1079 else
1080 gtk_entry_set_text(prop_dialog->name, "");
1082 gtk_option_menu_set_history(prop_dialog->dir_omenu, assoc->direction);
1084 for (i=0;i<2;i++) {
1085 if (assoc->end[i].role != NULL)
1086 gtk_entry_set_text(prop_dialog->end[i].role, assoc->end[i].role);
1087 else
1088 gtk_entry_set_text(prop_dialog->end[i].role, "");
1090 if (assoc->end[i].multiplicity != NULL)
1091 gtk_entry_set_text(prop_dialog->end[i].multiplicity,
1092 assoc->end[i].multiplicity);
1093 else
1094 gtk_entry_set_text(prop_dialog->end[i].multiplicity, "");
1096 gtk_option_menu_set_history(prop_dialog->end[i].attr_visible_button,
1097 (gint)assoc->end[i].visibility);
1098 gtk_toggle_button_set_active(prop_dialog->end[i].draw_arrow,
1099 assoc->end[i].arrow);
1100 gtk_toggle_button_set_active(prop_dialog->end[i].aggregate,
1101 assoc->end[i].aggregate == AGGREGATE_NORMAL);
1102 gtk_toggle_button_set_active(prop_dialog->end[i].composition,
1103 assoc->end[i].aggregate == AGGREGATE_COMPOSITION);
1107 static void
1108 mutex_aggregate_callback(GtkWidget *widget,
1109 AssociationPropertiesDialog *prop_dialog)
1111 int i;
1112 GtkToggleButton *button;
1114 button = GTK_TOGGLE_BUTTON(widget);
1116 if (!button->active)
1117 return;
1119 for (i=0;i<2;i++) {
1120 if (prop_dialog->end[i].aggregate != button) {
1121 gtk_toggle_button_set_active(prop_dialog->end[i].aggregate, 0);
1123 if (prop_dialog->end[i].composition != button) {
1124 gtk_toggle_button_set_active(prop_dialog->end[i].composition, 0);
1129 static GtkWidget *
1130 association_get_properties(Association *assoc, gboolean is_default)
1132 AssociationPropertiesDialog *prop_dialog;
1133 GtkWidget *dialog;
1134 GtkWidget *frame;
1135 GtkWidget *entry;
1136 GtkWidget *hbox;
1137 GtkWidget *split_hbox;
1138 GtkWidget *vbox;
1139 GtkWidget *label;
1140 GtkWidget *omenu;
1141 GtkWidget *menu;
1142 GtkWidget *submenu;
1143 GtkWidget *menuitem;
1144 GtkWidget *checkbox;
1145 GSList *group;
1146 int i;
1148 if (assoc->properties_dialog == NULL) {
1150 prop_dialog = g_new(AssociationPropertiesDialog, 1);
1151 assoc->properties_dialog = prop_dialog;
1153 dialog = gtk_vbox_new(FALSE, 0);
1154 gtk_object_ref(GTK_OBJECT(dialog));
1155 gtk_object_sink(GTK_OBJECT(dialog));
1156 prop_dialog->dialog = dialog;
1158 /* Name entry: */
1159 hbox = gtk_hbox_new(FALSE, 5);
1160 label = gtk_label_new(_("Name:"));
1161 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1162 entry = gtk_entry_new();
1163 prop_dialog->name = GTK_ENTRY(entry);
1164 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1165 gtk_widget_grab_focus(entry);
1166 gtk_widget_show (label);
1167 gtk_widget_show (entry);
1168 gtk_box_pack_start (GTK_BOX (dialog), hbox, TRUE, TRUE, 0);
1169 gtk_widget_show(hbox);
1171 /* Direction entry: */
1172 hbox = gtk_hbox_new(FALSE, 5);
1173 label = gtk_label_new(_("Direction:"));
1174 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1176 omenu = gtk_option_menu_new ();
1177 menu = gtk_menu_new ();
1178 prop_dialog->dir_menu = GTK_MENU(menu);
1179 prop_dialog->dir_omenu = GTK_OPTION_MENU(omenu);
1180 submenu = NULL;
1181 group = NULL;
1183 menuitem = gtk_radio_menu_item_new_with_label (group, _("None"));
1184 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1185 GINT_TO_POINTER(ASSOC_NODIR));
1186 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1187 gtk_menu_append (GTK_MENU (menu), menuitem);
1188 gtk_widget_show (menuitem);
1190 menuitem = gtk_radio_menu_item_new_with_label (group, _("From A to B"));
1191 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1192 GINT_TO_POINTER(ASSOC_RIGHT));
1193 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1194 gtk_menu_append (GTK_MENU (menu), menuitem);
1195 gtk_widget_show (menuitem);
1197 menuitem = gtk_radio_menu_item_new_with_label (group, _("From B to A"));
1198 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1199 GINT_TO_POINTER(ASSOC_LEFT));
1200 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1201 gtk_menu_append (GTK_MENU (menu), menuitem);
1202 gtk_widget_show (menuitem);
1204 gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
1205 gtk_box_pack_start (GTK_BOX (hbox), omenu, FALSE, TRUE, 0);
1207 gtk_widget_show (label);
1208 gtk_widget_show (omenu);
1209 gtk_box_pack_start (GTK_BOX (dialog), hbox, TRUE, TRUE, 0);
1210 gtk_widget_show(hbox);
1212 split_hbox = gtk_hbox_new(TRUE, 5);
1213 gtk_box_pack_start (GTK_BOX (dialog), split_hbox, TRUE, TRUE, 0);
1214 gtk_widget_show(split_hbox);
1216 group = NULL; /* For the radio-buttons */
1218 for (i=0;i<2;i++) {
1219 char *str;
1220 if (i==0)
1221 str = _("Side A");
1222 else
1223 str = _("Side B");
1224 frame = gtk_frame_new(str);
1226 vbox = gtk_vbox_new(FALSE, 5);
1227 /* End 'i' into vbox: */
1228 if (i==0)
1229 label = gtk_label_new(_("Side A"));
1230 else
1231 label = gtk_label_new(_("Side B"));
1233 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1235 /* Role entry: */
1236 hbox = gtk_hbox_new(FALSE, 5);
1237 label = gtk_label_new(_("Role:"));
1238 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1239 entry = gtk_entry_new();
1240 prop_dialog->end[i].role = GTK_ENTRY(entry);
1241 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1242 gtk_widget_show (label);
1243 gtk_widget_show (entry);
1244 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1245 gtk_widget_show(hbox);
1247 /* Multiplicity entry: */
1248 hbox = gtk_hbox_new(FALSE, 5);
1249 label = gtk_label_new(_("Multiplicity:"));
1250 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1251 entry = gtk_entry_new();
1252 prop_dialog->end[i].multiplicity = GTK_ENTRY(entry);
1253 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1254 gtk_widget_show (label);
1255 gtk_widget_show (entry);
1256 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1257 gtk_widget_show(hbox);
1259 hbox = gtk_hbox_new(FALSE, 5);
1260 label = gtk_label_new(_("Visibility:"));
1261 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1263 omenu = gtk_option_menu_new ();
1264 menu = gtk_menu_new ();
1265 prop_dialog->end[i].attr_visible = GTK_MENU(menu);
1266 prop_dialog->end[i].attr_visible_button = GTK_OPTION_MENU(omenu);
1267 submenu = NULL;
1268 group = NULL;
1269 menuitem = gtk_radio_menu_item_new_with_label (group, _("Public"));
1271 gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1272 GTK_SIGNAL_FUNC (attributes_update), umlclass);
1274 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1275 GINT_TO_POINTER(UML_PUBLIC) );
1276 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1277 gtk_menu_append (GTK_MENU (menu), menuitem);
1278 gtk_widget_show (menuitem);
1279 menuitem = gtk_radio_menu_item_new_with_label (group, _("Private"));
1281 gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1282 GTK_SIGNAL_FUNC (attributes_update), umlclass);
1284 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1285 GINT_TO_POINTER(UML_PRIVATE) );
1286 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1287 gtk_menu_append (GTK_MENU (menu), menuitem);
1288 gtk_widget_show (menuitem);
1289 menuitem = gtk_radio_menu_item_new_with_label (group, _("Protected"));
1291 gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1292 GTK_SIGNAL_FUNC (attributes_update), umlclass);
1294 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1295 GINT_TO_POINTER(UML_PROTECTED) );
1296 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1297 gtk_menu_append (GTK_MENU (menu), menuitem);
1298 gtk_widget_show (menuitem);
1299 menuitem = gtk_radio_menu_item_new_with_label (group, _("Implementation"));
1301 gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1302 GTK_SIGNAL_FUNC (attributes_update), umlclass);
1304 gtk_object_set_user_data(GTK_OBJECT(menuitem),
1305 GINT_TO_POINTER(UML_IMPLEMENTATION) );
1306 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem));
1307 gtk_menu_append (GTK_MENU (menu), menuitem);
1308 gtk_widget_show (menuitem);
1310 gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
1311 gtk_box_pack_start (GTK_BOX (hbox), omenu, FALSE, TRUE, 0);
1312 gtk_widget_show(label);
1313 gtk_widget_show(omenu);
1314 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1315 gtk_widget_show(hbox);
1317 /* Show arrow: */
1318 checkbox = gtk_check_button_new_with_label(_("Show arrow"));
1319 prop_dialog->end[i].draw_arrow = GTK_TOGGLE_BUTTON( checkbox );
1320 gtk_widget_show(checkbox);
1321 gtk_box_pack_start (GTK_BOX (vbox), checkbox, TRUE, TRUE, 0);
1323 /* Aggregate */
1324 checkbox = gtk_check_button_new_with_label(_("Aggregate"));
1325 prop_dialog->end[i].aggregate = GTK_TOGGLE_BUTTON( checkbox );
1326 gtk_signal_connect(GTK_OBJECT(checkbox), "toggled",
1327 (GtkSignalFunc) mutex_aggregate_callback, prop_dialog);
1328 gtk_widget_show(checkbox);
1329 gtk_box_pack_start (GTK_BOX (vbox), checkbox, TRUE, TRUE, 0);
1331 /* Composition */
1332 checkbox = gtk_check_button_new_with_label(_("Composition"));
1333 prop_dialog->end[i].composition = GTK_TOGGLE_BUTTON( checkbox );
1334 gtk_signal_connect(GTK_OBJECT(checkbox), "toggled",
1335 (GtkSignalFunc) mutex_aggregate_callback, prop_dialog);
1336 gtk_widget_show(checkbox);
1337 gtk_box_pack_start (GTK_BOX (vbox), checkbox, TRUE, TRUE, 0);
1339 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1340 gtk_container_add(GTK_CONTAINER(frame), vbox);
1341 gtk_box_pack_start (GTK_BOX (split_hbox), frame, TRUE, TRUE, 0);
1342 gtk_widget_show(vbox);
1343 gtk_widget_show(frame);
1347 fill_in_dialog(assoc);
1348 gtk_widget_show (assoc->properties_dialog->dialog);
1350 return assoc->properties_dialog->dialog;