re-adding .pngs as binary
[dia.git] / app / object_ops.c
blob4df5ea5a98ac08dd1859fba6358c82570a31d298
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.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdlib.h>
25 #include "object_ops.h"
26 #include "connectionpoint_ops.h"
27 #include "handle_ops.h"
28 #include "message.h"
30 #define OBJECT_CONNECT_DISTANCE 4.5
32 void
33 object_add_updates(Object *obj, Diagram *dia)
35 int i;
37 /* Bounding box */
38 diagram_add_update(dia, &obj->bounding_box);
40 /* Handles */
41 for (i=0;i<obj->num_handles;i++) {
42 handle_add_update(obj->handles[i], dia);
45 /* Connection points */
46 for (i=0;i<obj->num_connections;i++) {
47 connectionpoint_add_update(obj->connections[i], dia);
52 void
53 object_add_updates_list(GList *list, Diagram *dia)
55 Object *obj;
57 while (list != NULL) {
58 obj = (Object *)list->data;
60 object_add_updates(obj, dia);
62 list = g_list_next(list);
66 ConnectionPoint *
67 object_find_connectpoint_display(DDisplay *ddisp, Point *pos)
69 real distance;
70 ConnectionPoint *connectionpoint;
72 distance =
73 diagram_find_closest_connectionpoint(ddisp->diagram, &connectionpoint, pos);
75 distance = ddisplay_transform_length(ddisp, distance);
76 if (distance < OBJECT_CONNECT_DISTANCE) {
77 return connectionpoint;
80 return NULL;
83 /* pushes undo info */
84 void
85 object_connect_display(DDisplay *ddisp, Object *obj, Handle *handle)
87 ConnectionPoint *connectionpoint;
89 if (handle == NULL)
90 return;
92 if (handle->connected_to == NULL) {
93 connectionpoint = object_find_connectpoint_display(ddisp, &handle->pos);
95 if (connectionpoint != NULL) {
96 Change *change = undo_connect(ddisp->diagram, obj, handle,
97 connectionpoint);
98 (change->apply)(change, ddisp->diagram);
103 static guint
104 pointer_hash(gpointer some_pointer)
106 return (guint) some_pointer;
109 GList *
110 object_copy_list(GList *list_orig)
112 GList *list_copy;
113 GList *list;
114 Object *obj;
115 Object *obj_copy;
116 GHashTable *hash_table;
117 int i;
119 hash_table = g_hash_table_new((GHashFunc) pointer_hash, NULL);
121 list = list_orig;
122 list_copy = NULL;
123 while (list != NULL) {
124 obj = (Object *)list->data;
125 obj_copy = obj->ops->copy(obj);
127 g_hash_table_insert(hash_table, obj, obj_copy);
129 list_copy = g_list_append(list_copy, obj_copy);
131 list = g_list_next(list);
134 /* Rebuild the connections between the objects in the list: */
135 list = list_orig;
136 while (list != NULL) {
137 obj = (Object *)list->data;
138 obj_copy = g_hash_table_lookup(hash_table, obj);
140 for (i=0;i<obj->num_handles;i++) {
141 ConnectionPoint *con_point;
142 con_point = obj->handles[i]->connected_to;
144 if ( con_point != NULL ) {
145 Object *other_obj;
146 Object *other_obj_copy;
147 int con_point_nr;
149 other_obj = con_point->object;
150 other_obj_copy = g_hash_table_lookup(hash_table, other_obj);
152 if (other_obj_copy == NULL)
153 break; /* other object was not on list. */
155 con_point_nr=0;
156 while (other_obj->connections[con_point_nr] != con_point) {
157 con_point_nr++;
160 object_connect(obj_copy, obj_copy->handles[i],
161 other_obj_copy->connections[con_point_nr]);
165 list = g_list_next(list);
168 g_hash_table_destroy(hash_table);
170 return list_copy;
173 Point
174 object_list_corner(GList *list)
176 Point p = {0.0,0.0};
177 Object *obj;
179 if (list == NULL)
180 return p;
182 obj = (Object *)list->data;
183 p.x = obj->bounding_box.left;
184 p.y = obj->bounding_box.top;
186 list = g_list_next(list);
188 while (list != NULL) {
189 obj = (Object *)list->data;
191 if (p.x > obj->bounding_box.left)
192 p.x = obj->bounding_box.left;
193 if (p.y > obj->bounding_box.top)
194 p.y = obj->bounding_box.top;
196 list = g_list_next(list);
199 return p;
202 extern void
203 object_list_move_delta(GList *objects, Point *delta)
205 GList *list;
206 Object *obj;
207 Point pos;
209 list = objects;
210 while (list != NULL) {
211 obj = (Object *) list->data;
213 pos = obj->position;
214 point_add(&pos, delta);
216 obj->ops->move(obj, &pos);
218 list = g_list_next(list);
223 static int
224 object_list_sort_vertical(const void *o1, const void *o2) {
225 Object *obj1 = *(Object **)o1;
226 Object *obj2 = *(Object **)o2;
228 return (obj1->bounding_box.bottom+obj1->bounding_box.top)/2 -
229 (obj2->bounding_box.bottom+obj2->bounding_box.top)/2;
233 Align objects by moving them vertically:
235 void
236 object_list_align_v(GList *objects, Diagram *dia, int align)
238 GList *list;
239 Point *orig_pos;
240 Point *dest_pos;
241 real y_pos = 0;
242 Object *obj;
243 Point pos;
244 real top, bottom, freespc;
245 int nobjs;
246 int i;
247 gboolean sort_alloc = FALSE;
249 if (objects==NULL)
250 return;
252 obj = (Object *) objects->data; /* First object */
254 top = obj->bounding_box.top;
255 bottom = obj->bounding_box.bottom;
256 freespc = bottom - top;
258 nobjs = 1;
259 list = objects->next;
260 while (list != NULL) {
261 obj = (Object *) list->data;
263 if (obj->bounding_box.top < top)
264 top = obj->bounding_box.top;
265 if (obj->bounding_box.bottom > bottom)
266 bottom = obj->bounding_box.bottom;
268 freespc += obj->bounding_box.bottom - obj->bounding_box.top;
269 nobjs++;
271 list = g_list_next(list);
275 * These alignments can alter the order of elements, so we need
276 * to sort them out by position.
278 if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) {
279 Object **object_array = (Object **)g_malloc(sizeof(Object*)*nobjs);
280 int i = 0;
282 list = objects;
283 while (list != NULL) {
284 obj = (Object *) list->data;
285 object_array[i] = obj;
286 i++;
287 list = g_list_next(list);
289 qsort(object_array, nobjs, sizeof(Object*), object_list_sort_vertical);
290 list = NULL;
291 for (i = 0; i < nobjs; i++) {
292 list = g_list_append(list, object_array[i]);
294 objects = list;
295 sort_alloc = TRUE; /* Must remember to free the list */
296 g_free(object_array);
299 switch (align) {
300 case DIA_ALIGN_TOP: /* TOP */
301 y_pos = top;
302 break;
303 case DIA_ALIGN_CENTER: /* CENTER */
304 y_pos = (top + bottom)/2.0;
305 break;
306 case DIA_ALIGN_BOTTOM: /* BOTTOM */
307 y_pos = bottom;
308 break;
309 case DIA_ALIGN_POSITION: /* OBJECT POSITION */
310 y_pos = (top + bottom)/2.0;
311 break;
312 case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */
313 freespc = (bottom - top - freespc)/(double)(nobjs - 1);
314 y_pos = top;
315 break;
316 case DIA_ALIGN_ADJACENT: /* ADJACENT */
317 y_pos = top;
318 break;
319 default:
320 message_warning("Wrong argument to object_list_align_v()\n");
323 dest_pos = g_new(Point, nobjs);
324 orig_pos = g_new(Point, nobjs);
326 i = 0;
327 list = objects;
328 while (list != NULL) {
329 obj = (Object *) list->data;
331 pos.x = obj->position.x;
333 switch (align) {
334 case DIA_ALIGN_TOP: /* TOP */
335 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
336 break;
337 case DIA_ALIGN_CENTER: /* CENTER */
338 pos.y = y_pos + obj->position.y - (obj->bounding_box.top + obj->bounding_box.bottom)/2.0;
339 break;
340 case DIA_ALIGN_BOTTOM: /* BOTTOM */
341 pos.y = y_pos - (obj->bounding_box.bottom - obj->position.y);
342 break;
343 case DIA_ALIGN_POSITION: /* OBJECT POSITION */
344 pos.y = y_pos;
345 break;
346 case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */
347 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
348 y_pos += obj->bounding_box.bottom - obj->bounding_box.top + freespc;
349 break;
350 case DIA_ALIGN_ADJACENT: /* ADJACENT */
351 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
352 y_pos += obj->bounding_box.bottom - obj->bounding_box.top;
353 break;
356 orig_pos[i] = obj->position;
357 dest_pos[i] = pos;
359 obj->ops->move(obj, &pos);
361 i++;
362 list = g_list_next(list);
365 undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects));
366 if (sort_alloc)
367 g_list_free(objects);
371 static int
372 object_list_sort_horizontal(const void *o1, const void *o2) {
373 Object *obj1 = *(Object **)o1;
374 Object *obj2 = *(Object **)o2;
376 return (obj1->bounding_box.right+obj1->bounding_box.left)/2 -
377 (obj2->bounding_box.right+obj2->bounding_box.left)/2;
381 Align objects by moving then horizontally
383 void
384 object_list_align_h(GList *objects, Diagram *dia, int align)
386 GList *list;
387 Point *orig_pos;
388 Point *dest_pos;
389 real x_pos = 0;
390 Object *obj;
391 Point pos;
392 real left, right, freespc = 0;
393 int nobjs;
394 int i;
395 gboolean sort_alloc = FALSE;
397 if (objects==NULL)
398 return;
400 obj = (Object *) objects->data; /* First object */
402 left = obj->bounding_box.left;
403 right = obj->bounding_box.right;
404 freespc = right - left;
406 nobjs = 1;
407 list = objects->next;
408 while (list != NULL) {
409 obj = (Object *) list->data;
411 if (obj->bounding_box.left < left)
412 left = obj->bounding_box.left;
413 if (obj->bounding_box.right > right)
414 right = obj->bounding_box.right;
416 freespc += obj->bounding_box.right - obj->bounding_box.left;
417 nobjs++;
419 list = g_list_next(list);
423 * These alignments can alter the order of elements, so we need
424 * to sort them out by position.
426 if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) {
427 Object **object_array = (Object **)g_malloc(sizeof(Object*)*nobjs);
428 int i = 0;
430 list = objects;
431 while (list != NULL) {
432 obj = (Object *) list->data;
433 object_array[i] = obj;
434 i++;
435 list = g_list_next(list);
437 qsort(object_array, nobjs, sizeof(Object*), object_list_sort_horizontal);
438 list = NULL;
439 for (i = 0; i < nobjs; i++) {
440 list = g_list_append(list, object_array[i]);
442 objects = list;
443 sort_alloc = TRUE;
444 g_free(object_array);
447 switch (align) {
448 case DIA_ALIGN_LEFT:
449 x_pos = left;
450 break;
451 case DIA_ALIGN_CENTER:
452 x_pos = (left + right)/2.0;
453 break;
454 case DIA_ALIGN_RIGHT:
455 x_pos = right;
456 break;
457 case DIA_ALIGN_POSITION:
458 x_pos = (left + right)/2.0;
459 break;
460 case DIA_ALIGN_EQUAL:
461 freespc = (right - left - freespc)/(double)(nobjs - 1);
462 x_pos = left;
463 break;
464 case DIA_ALIGN_ADJACENT:
465 x_pos = left;
466 break;
467 default:
468 message_warning("Wrong argument to object_list_align_h()\n");
471 dest_pos = g_new(Point, nobjs);
472 orig_pos = g_new(Point, nobjs);
474 i = 0;
475 list = objects;
476 while (list != NULL) {
477 obj = (Object *) list->data;
479 switch (align) {
480 case DIA_ALIGN_LEFT:
481 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
482 break;
483 case DIA_ALIGN_CENTER:
484 pos.x = x_pos + obj->position.x - (obj->bounding_box.left + obj->bounding_box.right)/2.0;
485 break;
486 case DIA_ALIGN_RIGHT:
487 pos.x = x_pos - (obj->bounding_box.right - obj->position.x);
488 break;
489 case DIA_ALIGN_POSITION:
490 pos.x = x_pos;
491 break;
492 case DIA_ALIGN_EQUAL:
493 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
494 x_pos += obj->bounding_box.right - obj->bounding_box.left + freespc;
495 break;
496 case DIA_ALIGN_ADJACENT:
497 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
498 x_pos += obj->bounding_box.right - obj->bounding_box.left;
499 break;
502 pos.y = obj->position.y;
504 orig_pos[i] = obj->position;
505 dest_pos[i] = pos;
507 obj->ops->move(obj, &pos);
509 i++;
510 list = g_list_next(list);
513 undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects));
514 if (sort_alloc)
515 g_list_free(objects);