1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
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.
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.
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.
31 #include "diarenderer.h"
32 #include "attributes.h"
34 #include "properties.h"
37 #include "stereotype.h"
39 #include "pixmaps/object.xpm"
41 typedef struct _Objet Objet
;
43 #define NUM_CONNECTIONS 9
48 ConnectionPoint connections
[NUM_CONNECTIONS
];
52 char *exstate
; /* used for explicit state */
69 #define OBJET_BORDERWIDTH 0.1
70 #define OBJET_ACTIVEBORDERWIDTH 0.2
71 #define OBJET_LINEWIDTH 0.05
72 #define OBJET_MARGIN_X 0.5
73 #define OBJET_MARGIN_Y 0.5
74 #define OBJET_MARGIN_M 0.4
75 #define OBJET_FONTHEIGHT 0.8
77 static real
objet_distance_from(Objet
*ob
, Point
*point
);
78 static void objet_select(Objet
*ob
, Point
*clicked_point
,
79 DiaRenderer
*interactive_renderer
);
80 static ObjectChange
* objet_move_handle(Objet
*ob
, Handle
*handle
,
81 Point
*to
, ConnectionPoint
*cp
,
82 HandleMoveReason reason
, ModifierKeys modifiers
);
83 static ObjectChange
* objet_move(Objet
*ob
, Point
*to
);
84 static void objet_draw(Objet
*ob
, DiaRenderer
*renderer
);
85 static DiaObject
*objet_create(Point
*startpoint
,
89 static void objet_destroy(Objet
*ob
);
90 static DiaObject
*objet_load(ObjectNode obj_node
, int version
,
91 const char *filename
);
92 static PropDescription
*objet_describe_props(Objet
*objet
);
93 static void objet_get_props(Objet
*objet
, GPtrArray
*props
);
94 static void objet_set_props(Objet
*objet
, GPtrArray
*props
);
95 static void objet_update_data(Objet
*ob
);
97 static ObjectTypeOps objet_type_ops
=
99 (CreateFunc
) objet_create
,
100 (LoadFunc
) objet_load
,/*using_properties*/ /* load */
101 (SaveFunc
) object_save_using_properties
, /* save */
102 (GetDefaultsFunc
) NULL
,
103 (ApplyDefaultsFunc
) NULL
106 /* Non-nice typo, needed for backwards compatibility. */
107 DiaObjectType objet_type
=
109 "UML - Objet", /* name */
111 (char **) object_xpm
, /* pixmap */
113 &objet_type_ops
/* ops */
116 DiaObjectType umlobject_type
=
118 "UML - Object", /* name */
120 (char **) object_xpm
, /* pixmap */
122 &objet_type_ops
/* ops */
125 static ObjectOps objet_ops
= {
126 (DestroyFunc
) objet_destroy
,
127 (DrawFunc
) objet_draw
,
128 (DistanceFunc
) objet_distance_from
,
129 (SelectFunc
) objet_select
,
130 (CopyFunc
) object_copy_using_properties
,
131 (MoveFunc
) objet_move
,
132 (MoveHandleFunc
) objet_move_handle
,
133 (GetPropertiesFunc
) object_create_props_dialog
,
134 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
135 (ObjectMenuFunc
) NULL
,
136 (DescribePropsFunc
) objet_describe_props
,
137 (GetPropsFunc
) objet_get_props
,
138 (SetPropsFunc
) objet_set_props
,
142 static PropDescription objet_props
[] = {
143 ELEMENT_COMMON_PROPERTIES
,
144 PROP_STD_TEXT_COLOUR_OPTIONAL
,
145 PROP_STD_LINE_COLOUR_OPTIONAL
,
146 PROP_STD_FILL_COLOUR_OPTIONAL
,
147 { "text", PROP_TYPE_TEXT
, 0, N_("Text"), NULL
, NULL
},
148 { "stereotype", PROP_TYPE_STRING
, PROP_FLAG_VISIBLE
,
149 N_("Stereotype"), NULL
, NULL
},
150 { "exstate", PROP_TYPE_STRING
, PROP_FLAG_VISIBLE
,
151 N_("Explicit state"),NULL
, NULL
},
152 { "attribstr", PROP_TYPE_MULTISTRING
, PROP_FLAG_VISIBLE
|PROP_FLAG_DONT_SAVE
,
153 N_("Attributes"),NULL
, GINT_TO_POINTER(6) },
154 { "attrib", PROP_TYPE_TEXT
, 0, NULL
,NULL
, NULL
},
155 { "is_active", PROP_TYPE_BOOL
, PROP_FLAG_VISIBLE
,
156 N_("Active object"),NULL
,NULL
},
157 { "show_attribs", PROP_TYPE_BOOL
, PROP_FLAG_VISIBLE
,
158 N_("Show attributes"),NULL
,NULL
},
159 { "multiple", PROP_TYPE_BOOL
, PROP_FLAG_VISIBLE
,
160 N_("Multiple instance"),NULL
,NULL
},
164 static PropDescription
*
165 objet_describe_props(Objet
*ob
)
167 if (objet_props
[0].quark
== 0) {
168 prop_desc_list_calculate_quarks(objet_props
);
173 static PropOffset objet_offsets
[] = {
174 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
175 { "text_colour",PROP_TYPE_COLOUR
,offsetof(Objet
, text_color
) },
176 { "line_colour",PROP_TYPE_COLOUR
,offsetof(Objet
, line_color
) },
177 { "fill_colour",PROP_TYPE_COLOUR
,offsetof(Objet
, fill_color
) },
178 { "name", PROP_TYPE_STRING
, offsetof(Objet
, exstate
) },
179 { "stereotype", PROP_TYPE_STRING
, offsetof(Objet
, stereotype
) },
180 { "text", PROP_TYPE_TEXT
, offsetof(Objet
, text
) },
181 { "exstate", PROP_TYPE_STRING
, offsetof(Objet
, exstate
) },
182 { "attribstr", PROP_TYPE_MULTISTRING
, offsetof(Objet
, attrib
)},
183 { "attrib", PROP_TYPE_TEXT
, offsetof(Objet
, attributes
)},
184 { "is_active", PROP_TYPE_BOOL
, offsetof(Objet
,is_active
)},
185 { "show_attribs", PROP_TYPE_BOOL
, offsetof(Objet
, show_attributes
)},
186 { "multiple", PROP_TYPE_BOOL
, offsetof(Objet
, is_multiple
)},
191 objet_get_props(Objet
* objet
, GPtrArray
*props
)
193 if (objet
->attrib
) g_free(objet
->attrib
);
194 objet
->attrib
= text_get_string_copy(objet
->attributes
);
196 object_get_props_from_offsets(&objet
->element
.object
,
197 objet_offsets
,props
);
201 objet_set_props(Objet
*objet
, GPtrArray
*props
)
203 object_set_props_from_offsets(&objet
->element
.object
,
204 objet_offsets
,props
);
205 apply_textstr_properties(props
,objet
->attributes
,"attrib",objet
->attrib
);
206 g_free(objet
->st_stereotype
);
207 objet
->st_stereotype
= NULL
;
208 objet_update_data(objet
);
212 objet_distance_from(Objet
*ob
, Point
*point
)
214 DiaObject
*obj
= &ob
->element
.object
;
215 return distance_rectangle_point(&obj
->bounding_box
, point
);
219 objet_select(Objet
*ob
, Point
*clicked_point
,
220 DiaRenderer
*interactive_renderer
)
222 text_set_cursor(ob
->text
, clicked_point
, interactive_renderer
);
223 text_grab_focus(ob
->text
, &ob
->element
.object
);
224 element_update_handles(&ob
->element
);
228 objet_move_handle(Objet
*ob
, Handle
*handle
,
229 Point
*to
, ConnectionPoint
*cp
,
230 HandleMoveReason reason
, ModifierKeys modifiers
)
233 assert(handle
!=NULL
);
236 assert(handle
->id
< 8);
242 objet_move(Objet
*ob
, Point
*to
)
244 ob
->element
.corner
= *to
;
245 objet_update_data(ob
);
251 objet_draw(Objet
*ob
, DiaRenderer
*renderer
)
253 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
260 assert(renderer
!= NULL
);
269 bw
= (ob
->is_active
) ? OBJET_ACTIVEBORDERWIDTH
: OBJET_BORDERWIDTH
;
271 renderer_ops
->set_fillstyle(renderer
, FILLSTYLE_SOLID
);
272 renderer_ops
->set_linewidth(renderer
, bw
);
273 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
277 p2
.x
= x
+w
; p2
.y
= y
+h
;
279 if (ob
->is_multiple
) {
280 p1
.x
+= OBJET_MARGIN_M
;
281 p2
.y
-= OBJET_MARGIN_M
;
282 renderer_ops
->fill_rect(renderer
,
285 renderer_ops
->draw_rect(renderer
,
288 p1
.x
-= OBJET_MARGIN_M
;
289 p1
.y
+= OBJET_MARGIN_M
;
290 p2
.x
-= OBJET_MARGIN_M
;
291 p2
.y
+= OBJET_MARGIN_M
;
295 renderer_ops
->fill_rect(renderer
,
298 renderer_ops
->draw_rect(renderer
,
303 text_draw(ob
->text
, renderer
);
305 if ((ob
->st_stereotype
!= NULL
) && (ob
->st_stereotype
[0] != '\0')) {
306 renderer_ops
->draw_string(renderer
,
308 &ob
->st_pos
, ALIGN_CENTER
,
312 if ((ob
->exstate
!= NULL
) && (ob
->exstate
[0] != '\0')) {
313 renderer_ops
->draw_string(renderer
,
315 &ob
->ex_pos
, ALIGN_CENTER
,
319 /* Is there a better way to underline? */
320 p1
.x
= x
+ (w
- ob
->text
->max_width
)/2;
321 p1
.y
= ob
->text
->position
.y
+ ob
->text
->descent
;
322 p2
.x
= p1
.x
+ ob
->text
->max_width
;
325 renderer_ops
->set_linewidth(renderer
, OBJET_LINEWIDTH
);
327 for (i
=0; i
<ob
->text
->numlines
; i
++) {
328 p1
.x
= x
+ (w
- ob
->text
->row_width
[i
])/2;
329 p2
.x
= p1
.x
+ ob
->text
->row_width
[i
];
330 renderer_ops
->draw_line(renderer
,
333 p1
.y
= p2
.y
+= ob
->text
->height
;
336 if (ob
->show_attributes
) {
337 p1
.x
= x
; p2
.x
= x
+ w
;
338 p1
.y
= p2
.y
= ob
->attributes
->position
.y
- ob
->attributes
->ascent
- OBJET_MARGIN_Y
;
340 renderer_ops
->set_linewidth(renderer
, bw
);
341 renderer_ops
->draw_line(renderer
,
345 text_draw(ob
->attributes
, renderer
);
350 objet_update_data(Objet
*ob
)
352 Element
*elem
= &ob
->element
;
353 DiaObject
*obj
= &elem
->object
;
358 text_calc_boundingbox(ob
->text
, NULL
);
359 ob
->stereotype
= remove_stereotype_from_string(ob
->stereotype
);
360 if (!ob
->st_stereotype
) {
361 ob
->st_stereotype
= string_to_stereotype(ob
->stereotype
);
364 font
= ob
->text
->font
;
365 h
= elem
->corner
.y
+ OBJET_MARGIN_Y
;
367 if (ob
->is_multiple
) {
371 if ((ob
->stereotype
!= NULL
) && (ob
->stereotype
[0] != '\0')) {
372 w
= dia_font_string_width(ob
->st_stereotype
, font
, OBJET_FONTHEIGHT
);
373 h
+= OBJET_FONTHEIGHT
;
375 h
+= OBJET_MARGIN_Y
/2.0;
378 w
= MAX(w
, ob
->text
->max_width
);
379 p1
.y
= h
+ ob
->text
->ascent
; /* position of text */
381 h
+= ob
->text
->height
*ob
->text
->numlines
;
383 if ((ob
->exstate
!= NULL
) && (ob
->exstate
[0] != '\0')) {
384 w
= MAX(w
, dia_font_string_width(ob
->exstate
, font
, OBJET_FONTHEIGHT
));
385 h
+= OBJET_FONTHEIGHT
;
391 if (ob
->show_attributes
) {
392 h
+= OBJET_MARGIN_Y
+ ob
->attributes
->ascent
;
393 p2
.x
= elem
->corner
.x
+ OBJET_MARGIN_X
;
395 text_set_position(ob
->attributes
, &p2
);
397 h
+= ob
->attributes
->height
*ob
->attributes
->numlines
;
399 w
= MAX(w
, ob
->attributes
->max_width
);
402 w
+= 2*OBJET_MARGIN_X
;
404 p1
.x
= elem
->corner
.x
+ w
/2.0;
405 text_set_position(ob
->text
, &p1
);
407 ob
->ex_pos
.x
= ob
->st_pos
.x
= p1
.x
;
410 if (ob
->is_multiple
) {
415 elem
->height
= h
- elem
->corner
.y
;
417 /* Update connections: */
418 element_update_connections_rectangle(elem
, ob
->connections
);
420 element_update_boundingbox(elem
);
421 obj
->position
= elem
->corner
;
422 element_update_handles(elem
);
426 objet_create(Point
*startpoint
,
438 ob
= g_malloc0(sizeof(Objet
));
442 obj
->type
= ¨object_type
;
444 obj
->ops
= &objet_ops
;
446 elem
->corner
= *startpoint
;
448 ob
->text_color
= color_black
;
449 ob
->line_color
= attributes_get_foreground();
450 ob
->fill_color
= attributes_get_background();
452 font
= dia_font_new_from_style(DIA_FONT_SANS
, OBJET_FONTHEIGHT
);
454 ob
->show_attributes
= FALSE
;
455 ob
->is_active
= FALSE
;
456 ob
->is_multiple
= FALSE
;
459 ob
->stereotype
= NULL
;
460 ob
->st_stereotype
= NULL
;
462 /* The text position is recalculated later */
465 ob
->attributes
= new_text("", font
, 0.8, &p
, &color_black
, ALIGN_LEFT
);
467 ob
->text
= new_text("", font
, 0.8, &p
, &color_black
, ALIGN_CENTER
);
469 dia_font_unref(font
);
471 element_init(elem
, 8, NUM_CONNECTIONS
);
473 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
474 obj
->connections
[i
] = &ob
->connections
[i
];
475 ob
->connections
[i
].object
= obj
;
476 ob
->connections
[i
].connected
= NULL
;
478 ob
->connections
[8].flags
= CP_FLAGS_MAIN
;
479 elem
->extra_spacing
.border_trans
= OBJET_BORDERWIDTH
/2.0;
480 objet_update_data(ob
);
483 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
489 return &ob
->element
.object
;
493 objet_destroy(Objet
*ob
)
495 text_destroy(ob
->text
);
496 text_destroy(ob
->attributes
);
498 g_free(ob
->stereotype
);
499 g_free(ob
->st_stereotype
);
503 element_destroy(&ob
->element
);
507 objet_load(ObjectNode obj_node
, int version
, const char *filename
)
509 return object_load_using_properties(&objet_type
,
510 obj_node
,version
,filename
);