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 /* Object 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 void group_move_handle(Group
*group
, Handle
*handle
, Point
*to
);
44 static void group_move(Group
*group
, Point
*to
);
45 static void group_draw(Group
*group
, DiaRenderer
*renderer
);
46 static void group_update_data(Group
*group
);
47 static void group_update_handles(Group
*group
);
48 static void group_destroy(Group
*group
);
49 static Object
*group_copy(Group
*group
);
50 static const PropDescription
*group_describe_props(Group
*group
);
51 static void group_get_props(Group
*group
, GPtrArray
*props
);
52 static void group_set_props(Group
*group
, GPtrArray
*props
);
54 static ObjectOps group_ops
= {
55 (DestroyFunc
) group_destroy
,
56 (DrawFunc
) group_draw
,
57 (DistanceFunc
) group_distance_from
,
58 (SelectFunc
) group_select
,
59 (CopyFunc
) group_copy
,
60 (MoveFunc
) group_move
,
61 (MoveHandleFunc
) group_move_handle
,
62 (GetPropertiesFunc
) object_create_props_dialog
,
63 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
64 (ObjectMenuFunc
) NULL
,
65 (DescribePropsFunc
) group_describe_props
,
66 (GetPropsFunc
) group_get_props
,
67 (SetPropsFunc
) group_set_props
70 ObjectType group_type
= {
77 group_distance_from(Group
*group
, Point
*point
)
85 list
= group
->objects
;
86 while (list
!= NULL
) {
87 obj
= (Object
*) list
->data
;
89 dist
= MIN(dist
, obj
->ops
->distance_from(obj
, point
));
91 list
= g_list_next(list
);
98 group_select(Group
*group
)
100 group_update_handles(group
);
104 group_update_handles(Group
*group
)
106 Rectangle
*bb
= &group
->object
.bounding_box
;
108 group
->resize_handles
[0].id
= HANDLE_RESIZE_NW
;
109 group
->resize_handles
[0].pos
.x
= bb
->left
;
110 group
->resize_handles
[0].pos
.y
= bb
->top
;
112 group
->resize_handles
[1].id
= HANDLE_RESIZE_N
;
113 group
->resize_handles
[1].pos
.x
= (bb
->left
+ bb
->right
) / 2.0;
114 group
->resize_handles
[1].pos
.y
= bb
->top
;
116 group
->resize_handles
[2].id
= HANDLE_RESIZE_NE
;
117 group
->resize_handles
[2].pos
.x
= bb
->right
;
118 group
->resize_handles
[2].pos
.y
= bb
->top
;
120 group
->resize_handles
[3].id
= HANDLE_RESIZE_W
;
121 group
->resize_handles
[3].pos
.x
= bb
->left
;
122 group
->resize_handles
[3].pos
.y
= (bb
->top
+ bb
->bottom
) / 2.0;
124 group
->resize_handles
[4].id
= HANDLE_RESIZE_E
;
125 group
->resize_handles
[4].pos
.x
= bb
->right
;
126 group
->resize_handles
[4].pos
.y
= (bb
->top
+ bb
->bottom
) / 2.0;
128 group
->resize_handles
[5].id
= HANDLE_RESIZE_SW
;
129 group
->resize_handles
[5].pos
.x
= bb
->left
;
130 group
->resize_handles
[5].pos
.y
= bb
->bottom
;
132 group
->resize_handles
[6].id
= HANDLE_RESIZE_S
;
133 group
->resize_handles
[6].pos
.x
= (bb
->left
+ bb
->right
) / 2.0;
134 group
->resize_handles
[6].pos
.y
= bb
->bottom
;
136 group
->resize_handles
[7].id
= HANDLE_RESIZE_SE
;
137 group
->resize_handles
[7].pos
.x
= bb
->right
;
138 group
->resize_handles
[7].pos
.y
= bb
->bottom
;
142 group_move_handle(Group
*group
, Handle
*handle
, Point
*to
)
147 group_move(Group
*group
, Point
*to
)
152 pos
= group
->object
.position
;
153 point_sub(&delta
, &pos
);
155 object_list_move_delta(group
->objects
, &delta
);
157 group_update_data(group
);
161 group_draw(Group
*group
, DiaRenderer
*renderer
)
166 list
= group
->objects
;
167 while (list
!= NULL
) {
168 obj
= (Object
*) list
->data
;
170 DIA_RENDERER_GET_CLASS(renderer
)->draw_object(renderer
, obj
);
172 list
= g_list_next(list
);
177 group_destroy_shallow(Object
*obj
)
179 Group
*group
= (Group
*)obj
;
181 g_free(obj
->handles
);
183 if (obj
->connections
)
184 g_free(obj
->connections
);
186 g_list_free(group
->objects
);
188 prop_desc_list_free_handler_chain(group
->pdesc
);
189 g_free(group
->pdesc
);
195 group_destroy(Group
*group
)
197 Object
*obj
= &group
->object
;
199 destroy_object_list(group
->objects
);
201 /* ConnectionPoints in the inner objects have already
202 been unconnected and freed. */
203 obj
->num_connections
= 0;
205 prop_desc_list_free_handler_chain(group
->pdesc
);
206 g_free(group
->pdesc
);
212 group_copy(Group
*group
)
215 Object
*newobj
, *obj
;
220 obj
= &group
->object
;
222 newgroup
= g_new0(Group
,1);
223 newobj
= &newgroup
->object
;
225 object_copy(obj
, newobj
);
228 newobj
->handles
[i
] = &newgroup
->resize_handles
[i
];
229 newgroup
->resize_handles
[i
] = group
->resize_handles
[i
];
232 newgroup
->objects
= object_copy_list(group
->objects
);
234 /* Build connectionpoints: */
236 list
= newgroup
->objects
;
237 while (list
!= NULL
) {
238 listobj
= (Object
*) list
->data
;
240 for (i
=0;i
<listobj
->num_connections
;i
++) {
241 newobj
->connections
[num_conn
++] = listobj
->connections
[i
];
244 list
= g_list_next(list
);
247 /* NULL out the property description field */
248 newgroup
->pdesc
= NULL
;
250 return (Object
*)newgroup
;
255 group_update_data(Group
*group
)
260 if (group
->objects
!= NULL
) {
261 list
= group
->objects
;
262 obj
= (Object
*) list
->data
;
263 group
->object
.bounding_box
= obj
->bounding_box
;
265 list
= g_list_next(list
);
266 while (list
!= NULL
) {
267 obj
= (Object
*) list
->data
;
269 rectangle_union(&group
->object
.bounding_box
, &obj
->bounding_box
);
271 list
= g_list_next(list
);
275 obj
= (Object
*) group
->objects
->data
;
277 /* Move group by the point of the first object, otherwise a group
278 with all objects on grid might be moved off grid. */
279 group
->object
.position
= obj
->position
;
280 /* group->object.position.x = group->object.bounding_box.left;
281 group->object.position.y = group->object.bounding_box.top; */
283 group_update_handles(group
);
286 /* Make sure there are no connections from objects to objects
287 * outside of the created group.
290 group_create(GList
*objects
)
293 Object
*obj
, *part_obj
;
298 group
= g_new(Group
,1);
299 obj
= &group
->object
;
301 obj
->type
= &group_type
;
303 obj
->ops
= &group_ops
;
305 group
->objects
= objects
;
309 /* Make new connections as references to object connections: */
312 while (list
!= NULL
) {
313 part_obj
= (Object
*) list
->data
;
315 num_conn
+= part_obj
->num_connections
;
317 list
= g_list_next(list
);
320 object_init(obj
, 8, num_conn
);
322 /* Make connectionpoints be that of the 'inner' objects: */
325 while (list
!= NULL
) {
326 part_obj
= (Object
*) list
->data
;
328 for (i
=0;i
<part_obj
->num_connections
;i
++) {
329 obj
->connections
[num_conn
++] = part_obj
->connections
[i
];
332 list
= g_list_next(list
);
336 obj
->handles
[i
] = &group
->resize_handles
[i
];
337 obj
->handles
[i
]->type
= HANDLE_NON_MOVABLE
;
338 obj
->handles
[i
]->connect_type
= HANDLE_NONCONNECTABLE
;
339 obj
->handles
[i
]->connected_to
= NULL
;
342 group_update_data(group
);
343 return (Object
*)group
;
347 group_objects(Object
*group
)
356 group_prop_event_deliver(Group
*group
, Property
*prop
)
359 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
360 Object
*obj
= tmp
->data
;
362 if (obj
->ops
->describe_props
) {
363 const PropDescription
*pdesc
,*plist
;
365 /* I'm sorry. I haven't found a working less ugly solution :( */
366 plist
= obj
->ops
->describe_props(obj
);
367 pdesc
= prop_desc_list_find_prop(plist
,prop
->name
);
368 if (pdesc
&& pdesc
->event_handler
) {
370 PropEventHandler hdl
= prop_desc_find_real_handler(pdesc
);
372 return hdl(obj
,prop
);
374 g_warning("dropped group event on prop %s, "
375 "final handler was NULL",prop
->name
);
381 g_warning("undelivered group property event for prop %s",prop
->name
);
385 static const PropDescription
*
386 group_describe_props(Group
*group
)
389 if (group
->pdesc
== NULL
) {
390 GList
*descs
= NULL
, *tmp
;
392 /* create list of property descriptions */
393 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
394 const PropDescription
*desc
= NULL
;
395 Object
*obj
= tmp
->data
;
397 desc
= object_get_prop_descriptions(obj
);
399 if (desc
) descs
= g_list_append(descs
, (gpointer
)desc
);
401 group
->pdesc
= prop_desc_lists_intersection(descs
);
402 g_list_free(descs
); /* XXX: haven't we got a leak here ? */
404 if (group
->pdesc
!= NULL
) {
405 /* hijack event delivery */
406 for (i
=0; group
->pdesc
[i
].name
!= NULL
; i
++) {
407 if (group
->pdesc
[i
].event_handler
)
408 prop_desc_insert_handler(&group
->pdesc
[i
],
409 (PropEventHandler
)group_prop_event_deliver
);
418 group_get_props(Group
*group
, GPtrArray
*props
)
422 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
423 Object
*obj
= tmp
->data
;
425 if (obj
->ops
->get_props
) {
426 obj
->ops
->get_props(obj
, props
);
432 group_set_props(Group
*group
, GPtrArray
*props
)
436 for (tmp
= group
->objects
; tmp
!= NULL
; tmp
= tmp
->next
) {
437 Object
*obj
= tmp
->data
;
439 if (obj
->ops
->set_props
) {
440 obj
->ops
->set_props(obj
, props
);