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.
30 #include "diarenderer.h"
31 #include "attributes.h"
33 #include "properties.h"
35 #include "pixmaps/case.xpm"
37 typedef struct _UsecasePropertiesDialog UsecasePropertiesDialog
;
38 typedef struct _Usecase Usecase
;
39 typedef struct _UsecaseState UsecaseState
;
41 #define NUM_CONNECTIONS 9
46 ConnectionPoint connections
[NUM_CONNECTIONS
];
58 struct _UsecasePropertiesDialog
{
61 GtkToggleButton
*text_out
;
62 GtkToggleButton
*collaboration
;
66 #define USECASE_WIDTH 3.25
67 #define USECASE_HEIGHT 2
68 #define USECASE_MIN_RATIO 1.5
69 #define USECASE_MAX_RATIO 3
70 #define USECASE_LINEWIDTH 0.1
71 #define USECASE_MARGIN_Y 0.3
73 static real
usecase_distance_from(Usecase
*usecase
, Point
*point
);
74 static void usecase_select(Usecase
*usecase
, Point
*clicked_point
,
75 DiaRenderer
*interactive_renderer
);
76 static ObjectChange
* usecase_move_handle(Usecase
*usecase
, Handle
*handle
,
77 Point
*to
, ConnectionPoint
*cp
,
78 HandleMoveReason reason
, ModifierKeys modifiers
);
79 static ObjectChange
* usecase_move(Usecase
*usecase
, Point
*to
);
80 static void usecase_draw(Usecase
*usecase
, DiaRenderer
*renderer
);
81 static DiaObject
*usecase_create(Point
*startpoint
,
85 static void usecase_destroy(Usecase
*usecase
);
86 static DiaObject
*usecase_load(ObjectNode obj_node
, int version
,
87 const char *filename
);
88 static void usecase_update_data(Usecase
*usecase
);
89 static PropDescription
*usecase_describe_props(Usecase
*usecase
);
90 static void usecase_get_props(Usecase
*usecase
, GPtrArray
*props
);
91 static void usecase_set_props(Usecase
*usecase
, GPtrArray
*props
);
93 static ObjectTypeOps usecase_type_ops
=
95 (CreateFunc
) usecase_create
,
96 (LoadFunc
) usecase_load
,/*using_properties*/ /* load */
97 (SaveFunc
) object_save_using_properties
, /* save */
98 (GetDefaultsFunc
) NULL
,
99 (ApplyDefaultsFunc
) NULL
102 DiaObjectType usecase_type
=
104 "UML - Usecase", /* name */
106 (char **) case_xpm
, /* pixmap */
108 &usecase_type_ops
/* ops */
111 static ObjectOps usecase_ops
= {
112 (DestroyFunc
) usecase_destroy
,
113 (DrawFunc
) usecase_draw
,
114 (DistanceFunc
) usecase_distance_from
,
115 (SelectFunc
) usecase_select
,
116 (CopyFunc
) object_copy_using_properties
,
117 (MoveFunc
) usecase_move
,
118 (MoveHandleFunc
) usecase_move_handle
,
119 (GetPropertiesFunc
) object_create_props_dialog
,
120 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
121 (ObjectMenuFunc
) NULL
,
122 (DescribePropsFunc
) usecase_describe_props
,
123 (GetPropsFunc
) usecase_get_props
,
124 (SetPropsFunc
) usecase_set_props
127 static PropDescription usecase_props
[] = {
128 ELEMENT_COMMON_PROPERTIES
,
129 PROP_STD_LINE_COLOUR_OPTIONAL
,
130 PROP_STD_FILL_COLOUR_OPTIONAL
,
131 { "text_outside", PROP_TYPE_BOOL
, PROP_FLAG_VISIBLE
,
132 N_("Text outside"), NULL
, NULL
},
133 { "collaboration", PROP_TYPE_BOOL
, PROP_FLAG_VISIBLE
,
134 N_("Collaboration"), NULL
, NULL
},
136 PROP_STD_TEXT_HEIGHT
,
137 PROP_STD_TEXT_COLOUR_OPTIONAL
,
138 { "text", PROP_TYPE_TEXT
, 0, N_("Text"), NULL
, NULL
},
143 static PropDescription
*
144 usecase_describe_props(Usecase
*usecase
)
146 if (usecase_props
[0].quark
== 0) {
147 prop_desc_list_calculate_quarks(usecase_props
);
149 return usecase_props
;
152 static PropOffset usecase_offsets
[] = {
153 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
154 {"line_colour", PROP_TYPE_COLOUR
, offsetof(Usecase
, line_color
) },
155 {"fill_colour", PROP_TYPE_COLOUR
, offsetof(Usecase
, fill_color
) },
156 {"text_outside", PROP_TYPE_BOOL
, offsetof(Usecase
, text_outside
) },
157 {"collaboration", PROP_TYPE_BOOL
, offsetof(Usecase
, collaboration
) },
158 {"text",PROP_TYPE_TEXT
,offsetof(Usecase
,text
)},
159 {"text_font",PROP_TYPE_FONT
,offsetof(Usecase
,attrs
.font
)},
160 {"text_height",PROP_TYPE_REAL
,offsetof(Usecase
,attrs
.height
)},
161 {"text_colour",PROP_TYPE_COLOUR
,offsetof(Usecase
,attrs
.color
)},
166 usecase_get_props(Usecase
* usecase
, GPtrArray
*props
)
168 text_get_attributes(usecase
->text
,&usecase
->attrs
);
169 object_get_props_from_offsets(&usecase
->element
.object
,
170 usecase_offsets
,props
);
174 usecase_set_props(Usecase
*usecase
, GPtrArray
*props
)
176 object_set_props_from_offsets(&usecase
->element
.object
,
177 usecase_offsets
,props
);
178 apply_textattr_properties(props
,usecase
->text
,"text",&usecase
->attrs
);
179 usecase_update_data(usecase
);
183 usecase_distance_from(Usecase
*usecase
, Point
*point
)
185 DiaObject
*obj
= &usecase
->element
.object
;
186 return distance_rectangle_point(&obj
->bounding_box
, point
);
190 usecase_select(Usecase
*usecase
, Point
*clicked_point
,
191 DiaRenderer
*interactive_renderer
)
193 text_set_cursor(usecase
->text
, clicked_point
, interactive_renderer
);
194 text_grab_focus(usecase
->text
, &usecase
->element
.object
);
195 element_update_handles(&usecase
->element
);
199 usecase_move_handle(Usecase
*usecase
, Handle
*handle
,
200 Point
*to
, ConnectionPoint
*cp
,
201 HandleMoveReason reason
, ModifierKeys modifiers
)
203 assert(usecase
!=NULL
);
204 assert(handle
!=NULL
);
207 assert(handle
->id
< 8);
213 usecase_move(Usecase
*usecase
, Point
*to
)
218 usecase
->element
.corner
= *to
;
219 h
= usecase
->text
->height
*usecase
->text
->numlines
;
222 p
.x
+= usecase
->element
.width
/2.0;
223 if (usecase
->text_outside
) {
224 p
.y
+= usecase
->element
.height
- h
+ usecase
->text
->ascent
;
226 p
.y
+= (usecase
->element
.height
- h
)/2.0 + usecase
->text
->ascent
;
228 text_set_position(usecase
->text
, &p
);
229 usecase_update_data(usecase
);
235 usecase_draw(Usecase
*usecase
, DiaRenderer
*renderer
)
237 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
242 assert(usecase
!= NULL
);
243 assert(renderer
!= NULL
);
245 elem
= &usecase
->element
;
250 if (usecase
->text_outside
) {
253 c
.x
= x
+ elem
->width
/2.0;
254 c
.y
= y
+ USECASE_HEIGHT
/ 2.0;
263 renderer_ops
->set_fillstyle(renderer
, FILLSTYLE_SOLID
);
264 renderer_ops
->set_linewidth(renderer
, USECASE_LINEWIDTH
);
266 if (usecase
->collaboration
)
267 renderer_ops
->set_linestyle(renderer
, LINESTYLE_DASHED
);
269 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
271 renderer_ops
->fill_ellipse(renderer
,
274 &usecase
->fill_color
);
275 renderer_ops
->draw_ellipse(renderer
,
278 &usecase
->line_color
);
280 text_draw(usecase
->text
, renderer
);
285 usecase_update_data(Usecase
*usecase
)
290 Element
*elem
= &usecase
->element
;
291 DiaObject
*obj
= &elem
->object
;
293 text_calc_boundingbox(usecase
->text
, NULL
);
294 w
= usecase
->text
->max_width
;
295 h
= usecase
->text
->height
*usecase
->text
->numlines
;
297 if (!usecase
->text_outside
) {
300 if (ratio
> USECASE_MAX_RATIO
)
301 ratio
= USECASE_MAX_RATIO
;
303 if (ratio
< USECASE_MIN_RATIO
) {
304 ratio
= USECASE_MIN_RATIO
;
311 if (r
.x
< USECASE_WIDTH
)
313 if (r
.y
< USECASE_HEIGHT
)
314 r
.y
= USECASE_HEIGHT
;
317 r
.y
= USECASE_HEIGHT
;
323 if (usecase
->text_outside
) {
324 elem
->width
= MAX(elem
->width
, w
);
325 elem
->height
+= h
+ USECASE_MARGIN_Y
;
330 c
.x
= elem
->corner
.x
+ elem
->width
/ 2.0;
331 c
.y
= elem
->corner
.y
+ r
.y
;
332 half
.x
= r
.x
* M_SQRT1_2
;
333 half
.y
= r
.y
* M_SQRT1_2
;
335 /* Update connections: */
336 usecase
->connections
[0].pos
.x
= c
.x
- half
.x
;
337 usecase
->connections
[0].pos
.y
= c
.y
- half
.y
;
338 usecase
->connections
[1].pos
.x
= c
.x
;
339 usecase
->connections
[1].pos
.y
= elem
->corner
.y
;
340 usecase
->connections
[2].pos
.x
= c
.x
+ half
.x
;
341 usecase
->connections
[2].pos
.y
= c
.y
- half
.y
;
342 usecase
->connections
[3].pos
.x
= c
.x
- r
.x
;
343 usecase
->connections
[3].pos
.y
= c
.y
;
344 usecase
->connections
[4].pos
.x
= c
.x
+ r
.x
;
345 usecase
->connections
[4].pos
.y
= c
.y
;
347 if (usecase
->text_outside
) {
348 usecase
->connections
[5].pos
.x
= elem
->corner
.x
;
349 usecase
->connections
[5].pos
.y
= elem
->corner
.y
+ elem
->height
;
350 usecase
->connections
[6].pos
.x
= c
.x
;
351 usecase
->connections
[6].pos
.y
= elem
->corner
.y
+ elem
->height
;
352 usecase
->connections
[7].pos
.x
= elem
->corner
.x
+ elem
->width
;
353 usecase
->connections
[7].pos
.y
= elem
->corner
.y
+ elem
->height
;
355 usecase
->connections
[5].pos
.x
= c
.x
- half
.x
;
356 usecase
->connections
[5].pos
.y
= c
.y
+ half
.y
;
357 usecase
->connections
[6].pos
.x
= c
.x
;
358 usecase
->connections
[6].pos
.y
= elem
->corner
.y
+ elem
->height
;
359 usecase
->connections
[7].pos
.x
= c
.x
+ half
.x
;
360 usecase
->connections
[7].pos
.y
= c
.y
+ half
.y
;
362 usecase
->connections
[8].pos
.x
= c
.x
;
363 usecase
->connections
[8].pos
.y
= c
.y
;
365 usecase
->connections
[0].directions
= DIR_NORTH
|DIR_WEST
;
366 usecase
->connections
[1].directions
= DIR_NORTH
;
367 usecase
->connections
[2].directions
= DIR_NORTH
|DIR_EAST
;
368 usecase
->connections
[3].directions
= DIR_WEST
;
369 usecase
->connections
[4].directions
= DIR_EAST
;
370 usecase
->connections
[5].directions
= DIR_SOUTH
|DIR_WEST
;
371 usecase
->connections
[6].directions
= DIR_SOUTH
;
372 usecase
->connections
[7].directions
= DIR_SOUTH
|DIR_EAST
;
373 usecase
->connections
[8].directions
= DIR_ALL
;
375 h
= usecase
->text
->height
*usecase
->text
->numlines
;
376 p
= usecase
->element
.corner
;
377 p
.x
+= usecase
->element
.width
/2.0;
378 if (usecase
->text_outside
) {
379 p
.y
+= usecase
->element
.height
- h
+ usecase
->text
->ascent
;
381 p
.y
+= (usecase
->element
.height
- h
)/2.0 + usecase
->text
->ascent
;
383 text_set_position(usecase
->text
, &p
);
385 element_update_boundingbox(elem
);
387 obj
->position
= elem
->corner
;
389 element_update_handles(elem
);
394 usecase_create(Point
*startpoint
,
406 usecase
= g_malloc0(sizeof(Usecase
));
407 elem
= &usecase
->element
;
410 obj
->type
= &usecase_type
;
411 obj
->ops
= &usecase_ops
;
412 elem
->corner
= *startpoint
;
413 elem
->width
= USECASE_WIDTH
;
414 elem
->height
= USECASE_HEIGHT
;
416 usecase
->line_color
= attributes_get_foreground();
417 usecase
->fill_color
= attributes_get_background();
419 font
= dia_font_new_from_style(DIA_FONT_SANS
, 0.8);
421 p
.x
+= USECASE_WIDTH
/2.0;
422 p
.y
+= USECASE_HEIGHT
/2.0;
424 usecase
->text
= new_text("", font
, 0.8, &p
, &color_black
, ALIGN_CENTER
);
425 dia_font_unref(font
);
426 text_get_attributes(usecase
->text
,&usecase
->attrs
);
427 usecase
->text_outside
= 0;
428 usecase
->collaboration
= 0;
429 element_init(elem
, 8, NUM_CONNECTIONS
);
431 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
432 obj
->connections
[i
] = &usecase
->connections
[i
];
433 usecase
->connections
[i
].object
= obj
;
434 usecase
->connections
[i
].connected
= NULL
;
436 usecase
->connections
[8].flags
= CP_FLAGS_MAIN
;
437 elem
->extra_spacing
.border_trans
= 0.0;
438 usecase_update_data(usecase
);
441 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
446 return &usecase
->element
.object
;
450 usecase_destroy(Usecase
*usecase
)
452 text_destroy(usecase
->text
);
454 element_destroy(&usecase
->element
);
458 usecase_load(ObjectNode obj_node
, int version
, const char *filename
)
460 return object_load_using_properties(&usecase_type
,
461 obj_node
,version
,filename
);