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.
25 #include "connectionpoint.h"
27 #include "properties.h"
28 #include "diarenderer.h"
31 /* DiaObject must be first because this is a 'subclass' of it. */
34 Handle resize_handles
[8];
38 PropDescription
*pdesc
;
41 static real
group_distance_from(Group
*group
, Point
*point
);
42 static void group_select(Group
*group
);
43 static ObjectChange
* group_move_handle(Group
*group
, Handle
*handle
, Point
*to
, ConnectionPoint
*cp
,
44 HandleMoveReason reason
, ModifierKeys modifiers
);
45 static ObjectChange
* group_move(Group
*group
, Point
*to
);
46 static void group_draw(Group
*group
, DiaRenderer
*renderer
);
47 static void group_update_data(Group
*group
);
48 static void group_update_handles(Group
*group
);
49 static void group_destroy(Group
*group
);
50 static DiaObject
*group_copy(Group
*group
);
51 static const PropDescription
*group_describe_props(Group
*group
);
52 static void group_get_props(Group
*group
, GPtrArray
*props
);
53 static void group_set_props(Group
*group
, GPtrArray
*props
);
55 static ObjectOps group_ops
= {
56 (DestroyFunc
) group_destroy
,
57 (DrawFunc
) group_draw
,
58 (DistanceFunc
) group_distance_from
,
59 (SelectFunc
) group_select
,
60 (CopyFunc
) group_copy
,
61 (MoveFunc
) group_move
,
62 (MoveHandleFunc
) group_move_handle
,
63 (GetPropertiesFunc
) object_create_props_dialog
,
64 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
65 (ObjectMenuFunc
) NULL
,
66 (DescribePropsFunc
) group_describe_props
,
67 (GetPropsFunc
) group_get_props
,
68 (SetPropsFunc
) group_set_props
71 DiaObjectType group_type
= {
78 group_distance_from(Group
*group
, Point
*point
)
86 list
= group
->objects
;
87 while (list
!= NULL
) {
88 obj
= (DiaObject
*) list
->data
;
90 dist
= MIN(dist
, obj
->ops
->distance_from(obj
, point
));
92 list
= g_list_next(list
);
99 group_select(Group
*group
)
101 group_update_handles(group
);
105 group_update_handles(Group
*group
)
107 Rectangle
*bb
= &group
->object
.bounding_box
;
109 group
->resize_handles
[0].id
= HANDLE_RESIZE_NW
;
110 group
->resize_handles
[0].pos
.x
= bb
->left
;
111 group
->resize_handles
[0].pos
.y
= bb
->top
;
113 group
->resize_handles
[1].id
= HANDLE_RESIZE_N
;
114 group
->resize_handles
[1].pos
.x
= (bb
->left
+ bb
->right
) / 2.0;
115 group
->resize_handles
[1].pos
.y
= bb
->top
;
117 group
->resize_handles
[2].id
= HANDLE_RESIZE_NE
;
118 group
->resize_handles
[2].pos
.x
= bb
->right
;
119 group
->resize_handles
[2].pos
.y
= bb
->top
;
121 group
->resize_handles
[3].id
= HANDLE_RESIZE_W
;
122 group
->resize_handles
[3].pos
.x
= bb
->left
;
123 group
->resize_handles
[3].pos
.y
= (bb
->top
+ bb
->bottom
) / 2.0;
125 group
->resize_handles
[4].id
= HANDLE_RESIZE_E
;
126 group
->resize_handles
[4].pos
.x
= bb
->right
;
127 group
->resize_handles
[4].pos
.y
= (bb
->top
+ bb
->bottom
) / 2.0;
129 group
->resize_handles
[5].id
= HANDLE_RESIZE_SW
;
130 group
->resize_handles
[5].pos
.x
= bb
->left
;
131 group
->resize_handles
[5].pos
.y
= bb
->bottom
;
133 group
->resize_handles
[6].id
= HANDLE_RESIZE_S
;
134 group
->resize_handles
[6].pos
.x
= (bb
->left
+ bb
->right
) / 2.0;
135 group
->resize_handles
[6].pos
.y
= bb
->bottom
;
137 group
->resize_handles
[7].id
= HANDLE_RESIZE_SE
;
138 group
->resize_handles
[7].pos
.x
= bb
->right
;
139 group
->resize_handles
[7].pos
.y
= bb
->bottom
;
143 group_move_handle(Group
*group
, Handle
*handle
, Point
*to
, ConnectionPoint
*cp
,
144 HandleMoveReason reason
, ModifierKeys modifiers
)
151 group_move(Group
*group
, Point
*to
)
156 pos
= group
->object
.position
;
157 point_sub(&delta
, &pos
);
159 object_list_move_delta(group
->objects
, &delta
);
161 group_update_data(group
);
167 group_draw(Group
*group
, DiaRenderer
*renderer
)
172 list
= group
->objects
;
173 while (list
!= NULL
) {
174 obj
= (DiaObject
*) list
->data
;
176 DIA_RENDERER_GET_CLASS(renderer
)->draw_object(renderer
, obj
);
178 list
= g_list_next(list
);
183 group_destroy_shallow(DiaObject
*obj
)
185 Group
*group
= (Group
*)obj
;
187 g_free(obj
->handles
);
189 if (obj
->connections
)
190 g_free(obj
->connections
);
192 g_list_free(group
->objects
);
194 prop_desc_list_free_handler_chain(group
->pdesc
);
195 g_free(group
->pdesc
);
201 group_destroy(Group
*group
)
203 DiaObject
*obj
= &group
->object
;
205 destroy_object_list(group
->objects
);
207 /* ConnectionPoints in the inner objects have already
208 been unconnected and freed. */
209 obj
->num_connections
= 0;
211 prop_desc_list_free_handler_chain(group
->pdesc
);
212 g_free(group
->pdesc
);
218 group_copy(Group
*group
)
221 DiaObject
*newobj
, *obj
;
226 obj
= &group
->object
;
228 newgroup
= g_new0(Group
,1);
229 newobj
= &newgroup
->object
;
231 object_copy(obj
, newobj
);
234 newobj
->handles
[i
] = &newgroup
->resize_handles
[i
];
235 newgroup
->resize_handles
[i
] = group
->resize_handles
[i
];
238 newgroup
->objects
= object_copy_list(group
->objects
);
240 /* Build connectionpoints: */
242 list
= newgroup
->objects
;
243 while (list
!= NULL
) {
244 listobj
= (DiaObject
*) list
->data
;
246 for (i
=0;i
<listobj
->num_connections
;i
++) {
247 newobj
->connections
[num_conn
++] = listobj
->connections
[i
];
250 list
= g_list_next(list
);
253 /* NULL out the property description field */
254 newgroup
->pdesc
= NULL
;
256 return (DiaObject
*)newgroup
;
261 group_update_data(Group
*group
)
266 if (group
->objects
!= NULL
) {
267 list
= group
->objects
;
268 obj
= (DiaObject
*) list
->data
;
269 group
->object
.bounding_box
= obj
->bounding_box
;
271 list
= g_list_next(list
);
272 while (list
!= NULL
) {
273 obj
= (DiaObject
*) list
->data
;
275 rectangle_union(&group
->object
.bounding_box
, &obj
->bounding_box
);
277 list
= g_list_next(list
);
280 obj
= (DiaObject
*) group
->objects
->data
;
282 /* Move group by the point of the first object, otherwise a group
283 with all objects on grid might be moved off grid. */
284 group
->object
.position
= obj
->position
;
286 group_update_handles(group
);
290 /* Make sure there are no connections from objects to objects
291 * outside of the created group.
294 group_create(GList
*objects
)
297 DiaObject
*obj
, *part_obj
;
302 group
= g_new0(Group
,1);
303 obj
= &group
->object
;
305 obj
->type
= &group_type
;
307 obj
->ops
= &group_ops
;
309 group
->objects
= objects
;
313 /* Make new connections as references to object connections: */
316 while (list
!= NULL
) {
317 part_obj
= (DiaObject
*) list
->data
;
319 num_conn
+= part_obj
->num_connections
;
321 list
= g_list_next(list
);
324 object_init(obj
, 8, num_conn
);
326 /* Make connectionpoints be that of the 'inner' objects: */
329 while (list
!= NULL
) {
330 part_obj
= (DiaObject
*) list
->data
;
332 for (i
=0;i
<part_obj
->num_connections
;i
++) {
333 obj
->connections
[num_conn
++] = part_obj
->connections
[i
];
336 list
= g_list_next(list
);
340 obj
->handles
[i
] = &group
->resize_handles
[i
];
341 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
342 obj
->handles
[i
]->connect_type
= HANDLE_NONCONNECTABLE
;
343 obj
->handles
[i
]->connected_to
= NULL
;
346 group_update_data(group
);
347 return (DiaObject
*)group
;
351 group_objects(DiaObject
*group
)
360 group_prop_event_deliver(Group
*group
, Property
*prop
)
363 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
364 DiaObject
*obj
= tmp
->data
;
366 if (obj
->ops
->describe_props
) {
367 const PropDescription
*pdesc
,*plist
;
369 /* I'm sorry. I haven't found a working less ugly solution :( */
370 plist
= obj
->ops
->describe_props(obj
);
371 pdesc
= prop_desc_list_find_prop(plist
,prop
->name
);
372 if (pdesc
&& pdesc
->event_handler
) {
374 PropEventHandler hdl
= prop_desc_find_real_handler(pdesc
);
376 return hdl(obj
,prop
);
378 g_warning("dropped group event on prop %s, "
379 "final handler was NULL",prop
->name
);
385 g_warning("undelivered group property event for prop %s",prop
->name
);
389 static const PropDescription
*
390 group_describe_props(Group
*group
)
393 if (group
->pdesc
== NULL
) {
394 GList
*descs
= NULL
, *tmp
;
396 /* create list of property descriptions */
397 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
398 const PropDescription
*desc
= NULL
;
399 DiaObject
*obj
= tmp
->data
;
401 desc
= object_get_prop_descriptions(obj
);
403 if (desc
) descs
= g_list_append(descs
, (gpointer
)desc
);
405 group
->pdesc
= prop_desc_lists_intersection(descs
);
406 g_list_free(descs
); /* XXX: haven't we got a leak here ? */
408 if (group
->pdesc
!= NULL
) {
409 /* hijack event delivery */
410 for (i
=0; group
->pdesc
[i
].name
!= NULL
; i
++) {
411 if (group
->pdesc
[i
].event_handler
)
412 prop_desc_insert_handler(&group
->pdesc
[i
],
413 (PropEventHandler
)group_prop_event_deliver
);
422 group_get_props(Group
*group
, GPtrArray
*props
)
426 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
427 DiaObject
*obj
= tmp
->data
;
429 if (obj
->ops
->get_props
) {
430 obj
->ops
->get_props(obj
, props
);
436 group_set_props(Group
*group
, GPtrArray
*props
)
440 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
441 DiaObject
*obj
= tmp
->data
;
443 if (obj
->ops
->set_props
) {
444 obj
->ops
->set_props(obj
, props
);