2005-09-05 Inaki Larranaga <dooteo@euskalgnu.org>
[dia.git] / lib / group.c
blobb8c541989715bf8eff5a3d8b55b08d1c505a7ebe
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 /* DiaObject must be first because this is a 'subclass' of it. */
32 DiaObject 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 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 = {
72 "Group",
74 NULL
77 static real
78 group_distance_from(Group *group, Point *point)
80 real dist;
81 GList *list;
82 DiaObject *obj;
84 dist = 100000.0;
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);
95 return dist;
98 static void
99 group_select(Group *group)
101 group_update_handles(group);
104 static void
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;
142 static ObjectChange*
143 group_move_handle(Group *group, Handle *handle, Point *to, ConnectionPoint *cp,
144 HandleMoveReason reason, ModifierKeys modifiers)
147 return NULL;
150 static ObjectChange*
151 group_move(Group *group, Point *to)
153 Point delta,pos;
155 delta = *to;
156 pos = group->object.position;
157 point_sub(&delta, &pos);
159 object_list_move_delta(group->objects, &delta);
161 group_update_data(group);
163 return NULL;
166 static void
167 group_draw(Group *group, DiaRenderer *renderer)
169 GList *list;
170 DiaObject *obj;
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);
182 void
183 group_destroy_shallow(DiaObject *obj)
185 Group *group = (Group *)obj;
186 if (obj->handles)
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);
197 g_free(group);
200 static void
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);
214 object_destroy(obj);
217 static DiaObject *
218 group_copy(Group *group)
220 Group *newgroup;
221 DiaObject *newobj, *obj;
222 DiaObject *listobj;
223 GList *list;
224 int num_conn, i;
226 obj = &group->object;
228 newgroup = g_new0(Group,1);
229 newobj = &newgroup->object;
231 object_copy(obj, newobj);
233 for (i=0;i<8;i++) {
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: */
241 num_conn = 0;
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;
260 static void
261 group_update_data(Group *group)
263 GList *list;
264 DiaObject *obj;
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.
293 DiaObject *
294 group_create(GList *objects)
296 Group *group;
297 DiaObject *obj, *part_obj;
298 int i;
299 GList *list;
300 int num_conn;
302 group = g_new0(Group,1);
303 obj = &group->object;
305 obj->type = &group_type;
307 obj->ops = &group_ops;
309 group->objects = objects;
311 group->pdesc = NULL;
313 /* Make new connections as references to object connections: */
314 num_conn = 0;
315 list = objects;
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: */
327 num_conn = 0;
328 list = 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);
339 for (i=0;i<8;i++) {
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;
350 GList *
351 group_objects(DiaObject *group)
353 Group *g;
354 g = (Group *)group;
356 return g->objects;
359 static gboolean
360 group_prop_event_deliver(Group *group, Property *prop)
362 GList *tmp;
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) {
373 /* deliver */
374 PropEventHandler hdl = prop_desc_find_real_handler(pdesc);
375 if (hdl) {
376 return hdl(obj,prop);
377 } else {
378 g_warning("dropped group event on prop %s, "
379 "final handler was NULL",prop->name);
380 return FALSE;
385 g_warning("undelivered group property event for prop %s",prop->name);
386 return FALSE;
389 static const PropDescription *
390 group_describe_props(Group *group)
392 int i;
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);
418 return group->pdesc;
421 static void
422 group_get_props(Group *group, GPtrArray *props)
424 GList *tmp;
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);
435 static void
436 group_set_props(Group *group, GPtrArray *props)
438 GList *tmp;
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);