don't include dlfcn.h on win32
[dia.git] / lib / group.c
blob0cfce6757523cea0b9d8ad9681833d91d3a5ab89
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.
18 #include <config.h>
20 #include <assert.h>
21 #include <gtk/gtk.h>
22 #include <math.h>
24 #include "object.h"
25 #include "connectionpoint.h"
26 #include "group.h"
27 #include "properties.h"
28 #include "diarenderer.h"
30 struct _Group {
31 /* Object must be first because this is a 'subclass' of it. */
32 Object object;
34 Handle resize_handles[8];
36 GList *objects;
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 = {
71 "Group",
73 NULL
76 static real
77 group_distance_from(Group *group, Point *point)
79 real dist;
80 GList *list;
81 Object *obj;
83 dist = 100000.0;
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);
94 return dist;
97 static void
98 group_select(Group *group)
100 group_update_handles(group);
103 static void
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;
141 static void
142 group_move_handle(Group *group, Handle *handle, Point *to)
146 static void
147 group_move(Group *group, Point *to)
149 Point delta,pos;
151 delta = *to;
152 pos = group->object.position;
153 point_sub(&delta, &pos);
155 object_list_move_delta(group->objects, &delta);
157 group_update_data(group);
160 static void
161 group_draw(Group *group, DiaRenderer *renderer)
163 GList *list;
164 Object *obj;
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);
176 void
177 group_destroy_shallow(Object *obj)
179 Group *group = (Group *)obj;
180 if (obj->handles)
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);
191 g_free(group);
194 static void
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);
208 object_destroy(obj);
211 static Object *
212 group_copy(Group *group)
214 Group *newgroup;
215 Object *newobj, *obj;
216 Object *listobj;
217 GList *list;
218 int num_conn, i;
220 obj = &group->object;
222 newgroup = g_new0(Group,1);
223 newobj = &newgroup->object;
225 object_copy(obj, newobj);
227 for (i=0;i<8;i++) {
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: */
235 num_conn = 0;
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;
254 static void
255 group_update_data(Group *group)
257 GList *list;
258 Object *obj;
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.
289 Object *
290 group_create(GList *objects)
292 Group *group;
293 Object *obj, *part_obj;
294 int i;
295 GList *list;
296 int num_conn;
298 group = g_new(Group,1);
299 obj = &group->object;
301 obj->type = &group_type;
303 obj->ops = &group_ops;
305 group->objects = objects;
307 group->pdesc = NULL;
309 /* Make new connections as references to object connections: */
310 num_conn = 0;
311 list = objects;
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: */
323 num_conn = 0;
324 list = 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);
335 for (i=0;i<8;i++) {
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;
346 GList *
347 group_objects(Object *group)
349 Group *g;
350 g = (Group *)group;
352 return g->objects;
355 static gboolean
356 group_prop_event_deliver(Group *group, Property *prop)
358 GList *tmp;
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) {
369 /* deliver */
370 PropEventHandler hdl = prop_desc_find_real_handler(pdesc);
371 if (hdl) {
372 return hdl(obj,prop);
373 } else {
374 g_warning("dropped group event on prop %s, "
375 "final handler was NULL",prop->name);
376 return FALSE;
381 g_warning("undelivered group property event for prop %s",prop->name);
382 return FALSE;
385 static const PropDescription *
386 group_describe_props(Group *group)
388 int i;
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);
414 return group->pdesc;
417 static void
418 group_get_props(Group *group, GPtrArray *props)
420 GList *tmp;
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);
431 static void
432 group_set_props(Group *group, GPtrArray *props)
434 GList *tmp;
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);