* installer/win32/dia.nsi: Save diagrams by default in "My Pictures";
[dia.git] / app / object_ops.c
blobddb492c04528f01842a9cde52d6c34c6047c474a
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(DiaObject *obj, Diagram *dia)
35 int i;
37 /* Bounding box */
38 if (obj->highlight_color != NULL) {
39 diagram_add_update_with_border(dia, &obj->bounding_box, 5);
40 } else {
41 diagram_add_update(dia, &obj->bounding_box);
44 /* Handles */
45 for (i=0;i<obj->num_handles;i++) {
46 handle_add_update(obj->handles[i], dia);
49 /* Connection points */
50 for (i=0;i<obj->num_connections;i++) {
51 connectionpoint_add_update(obj->connections[i], dia);
56 void
57 object_add_updates_list(GList *list, Diagram *dia)
59 DiaObject *obj;
61 while (list != NULL) {
62 obj = (DiaObject *)list->data;
64 object_add_updates(obj, dia);
66 list = g_list_next(list);
70 /** Find a connectionpoint sufficiently close to the given point.
72 * @param ddisp The display to search
73 * @param pos A position in the display, typically mouse position
74 * @param notthis If not null, an object to ignore (typically the object
75 * connecting)
76 * @param snap_to_objects Whether snapping to objects should be in effect
77 * in this call (anded to the display-wide setting).
79 ConnectionPoint *
80 object_find_connectpoint_display(DDisplay *ddisp, Point *pos,
81 DiaObject *notthis, gboolean snap_to_objects)
83 real distance;
84 ConnectionPoint *connectionpoint;
85 GList *avoid = NULL;
86 DiaObject *obj_here;
88 distance =
89 diagram_find_closest_connectionpoint(ddisp->diagram, &connectionpoint,
90 pos, notthis);
92 distance = ddisplay_transform_length(ddisp, distance);
93 if (distance < OBJECT_CONNECT_DISTANCE) {
94 return connectionpoint;
96 if (ddisp->mainpoint_magnetism && snap_to_objects) {
97 DiaObject *parent;
98 /* Try to find an all-object CP. */
99 /* Don't pick a parent, though */
100 avoid = g_list_prepend(avoid, notthis);
101 for (parent = notthis->parent; parent != NULL; parent = parent->parent) {
102 avoid = g_list_prepend(avoid, parent);
104 obj_here = diagram_find_clicked_object_except(ddisp->diagram, pos, 0.00001, avoid);
105 if (obj_here != NULL) {
106 int i;
107 for (i = 0; i < obj_here->num_connections; i++) {
108 if (obj_here->connections[i]->flags & CP_FLAG_ANYPLACE) {
109 g_list_free(avoid);
110 return obj_here->connections[i];
116 return NULL;
119 /* pushes undo info */
120 void
121 object_connect_display(DDisplay *ddisp, DiaObject *obj, Handle *handle,
122 gboolean snap_to_objects)
124 ConnectionPoint *connectionpoint;
126 if (handle == NULL)
127 return;
129 if (handle->connected_to == NULL) {
130 connectionpoint = object_find_connectpoint_display(ddisp, &handle->pos,
131 obj, snap_to_objects);
133 if (connectionpoint != NULL) {
134 Change *change = undo_connect(ddisp->diagram, obj, handle,
135 connectionpoint);
136 (change->apply)(change, ddisp->diagram);
141 Point
142 object_list_corner(GList *list)
144 Point p = {0.0,0.0};
145 DiaObject *obj;
147 if (list == NULL)
148 return p;
150 obj = (DiaObject *)list->data;
151 p.x = obj->bounding_box.left;
152 p.y = obj->bounding_box.top;
154 list = g_list_next(list);
156 while (list != NULL) {
157 obj = (DiaObject *)list->data;
159 if (p.x > obj->bounding_box.left)
160 p.x = obj->bounding_box.left;
161 if (p.y > obj->bounding_box.top)
162 p.y = obj->bounding_box.top;
164 list = g_list_next(list);
167 return p;
170 static int
171 object_list_sort_vertical(const void *o1, const void *o2) {
172 DiaObject *obj1 = *(DiaObject **)o1;
173 DiaObject *obj2 = *(DiaObject **)o2;
175 return (obj1->bounding_box.bottom+obj1->bounding_box.top)/2 -
176 (obj2->bounding_box.bottom+obj2->bounding_box.top)/2;
180 Align objects by moving them vertically:
182 void
183 object_list_align_v(GList *objects, Diagram *dia, int align)
185 GList *list;
186 Point *orig_pos;
187 Point *dest_pos;
188 real y_pos = 0;
189 DiaObject *obj;
190 Point pos;
191 real top, bottom, freespc;
192 int nobjs;
193 int i;
194 gboolean sort_alloc = FALSE;
196 if (objects==NULL)
197 return;
199 obj = (DiaObject *) objects->data; /* First object */
201 top = obj->bounding_box.top;
202 bottom = obj->bounding_box.bottom;
203 freespc = bottom - top;
205 nobjs = 1;
206 list = objects->next;
207 while (list != NULL) {
208 obj = (DiaObject *) list->data;
210 if (obj->bounding_box.top < top)
211 top = obj->bounding_box.top;
212 if (obj->bounding_box.bottom > bottom)
213 bottom = obj->bounding_box.bottom;
215 freespc += obj->bounding_box.bottom - obj->bounding_box.top;
216 nobjs++;
218 list = g_list_next(list);
222 * These alignments can alter the order of elements, so we need
223 * to sort them out by position.
225 if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) {
226 DiaObject **object_array = (DiaObject **)g_malloc(sizeof(DiaObject*)*nobjs);
227 int i = 0;
229 list = objects;
230 while (list != NULL) {
231 obj = (DiaObject *) list->data;
232 object_array[i] = obj;
233 i++;
234 list = g_list_next(list);
236 qsort(object_array, nobjs, sizeof(DiaObject*), object_list_sort_vertical);
237 list = NULL;
238 for (i = 0; i < nobjs; i++) {
239 list = g_list_append(list, object_array[i]);
241 objects = list;
242 sort_alloc = TRUE; /* Must remember to free the list */
243 g_free(object_array);
246 switch (align) {
247 case DIA_ALIGN_TOP: /* TOP */
248 y_pos = top;
249 break;
250 case DIA_ALIGN_CENTER: /* CENTER */
251 y_pos = (top + bottom)/2.0;
252 break;
253 case DIA_ALIGN_BOTTOM: /* BOTTOM */
254 y_pos = bottom;
255 break;
256 case DIA_ALIGN_POSITION: /* OBJECT POSITION */
257 y_pos = (top + bottom)/2.0;
258 break;
259 case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */
260 freespc = (bottom - top - freespc)/(double)(nobjs - 1);
261 y_pos = top;
262 break;
263 case DIA_ALIGN_ADJACENT: /* ADJACENT */
264 y_pos = top;
265 break;
266 default:
267 message_warning("Wrong argument to object_list_align_v()\n");
270 dest_pos = g_new(Point, nobjs);
271 orig_pos = g_new(Point, nobjs);
273 i = 0;
274 list = objects;
275 while (list != NULL) {
276 obj = (DiaObject *) list->data;
278 pos.x = obj->position.x;
280 switch (align) {
281 case DIA_ALIGN_TOP: /* TOP */
282 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
283 break;
284 case DIA_ALIGN_CENTER: /* CENTER */
285 pos.y = y_pos + obj->position.y - (obj->bounding_box.top + obj->bounding_box.bottom)/2.0;
286 break;
287 case DIA_ALIGN_BOTTOM: /* BOTTOM */
288 pos.y = y_pos - (obj->bounding_box.bottom - obj->position.y);
289 break;
290 case DIA_ALIGN_POSITION: /* OBJECT POSITION */
291 pos.y = y_pos;
292 break;
293 case DIA_ALIGN_EQUAL: /* EQUAL DISTANCE */
294 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
295 y_pos += obj->bounding_box.bottom - obj->bounding_box.top + freespc;
296 break;
297 case DIA_ALIGN_ADJACENT: /* ADJACENT */
298 pos.y = y_pos + obj->position.y - obj->bounding_box.top;
299 y_pos += obj->bounding_box.bottom - obj->bounding_box.top;
300 break;
303 orig_pos[i] = obj->position;
304 dest_pos[i] = pos;
306 obj->ops->move(obj, &pos);
308 i++;
309 list = g_list_next(list);
312 undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects));
313 if (sort_alloc)
314 g_list_free(objects);
318 static int
319 object_list_sort_horizontal(const void *o1, const void *o2) {
320 DiaObject *obj1 = *(DiaObject **)o1;
321 DiaObject *obj2 = *(DiaObject **)o2;
323 return (obj1->bounding_box.right+obj1->bounding_box.left)/2 -
324 (obj2->bounding_box.right+obj2->bounding_box.left)/2;
328 Align objects by moving then horizontally
330 void
331 object_list_align_h(GList *objects, Diagram *dia, int align)
333 GList *list;
334 Point *orig_pos;
335 Point *dest_pos;
336 real x_pos = 0;
337 DiaObject *obj;
338 Point pos;
339 real left, right, freespc = 0;
340 int nobjs;
341 int i;
342 gboolean sort_alloc = FALSE;
344 if (objects==NULL)
345 return;
347 obj = (DiaObject *) objects->data; /* First object */
349 left = obj->bounding_box.left;
350 right = obj->bounding_box.right;
351 freespc = right - left;
353 nobjs = 1;
354 list = objects->next;
355 while (list != NULL) {
356 obj = (DiaObject *) list->data;
358 if (obj->bounding_box.left < left)
359 left = obj->bounding_box.left;
360 if (obj->bounding_box.right > right)
361 right = obj->bounding_box.right;
363 freespc += obj->bounding_box.right - obj->bounding_box.left;
364 nobjs++;
366 list = g_list_next(list);
370 * These alignments can alter the order of elements, so we need
371 * to sort them out by position.
373 if (align == DIA_ALIGN_EQUAL || align == DIA_ALIGN_ADJACENT) {
374 DiaObject **object_array = (DiaObject **)g_malloc(sizeof(DiaObject*)*nobjs);
375 int i = 0;
377 list = objects;
378 while (list != NULL) {
379 obj = (DiaObject *) list->data;
380 object_array[i] = obj;
381 i++;
382 list = g_list_next(list);
384 qsort(object_array, nobjs, sizeof(DiaObject*), object_list_sort_horizontal);
385 list = NULL;
386 for (i = 0; i < nobjs; i++) {
387 list = g_list_append(list, object_array[i]);
389 objects = list;
390 sort_alloc = TRUE;
391 g_free(object_array);
394 switch (align) {
395 case DIA_ALIGN_LEFT:
396 x_pos = left;
397 break;
398 case DIA_ALIGN_CENTER:
399 x_pos = (left + right)/2.0;
400 break;
401 case DIA_ALIGN_RIGHT:
402 x_pos = right;
403 break;
404 case DIA_ALIGN_POSITION:
405 x_pos = (left + right)/2.0;
406 break;
407 case DIA_ALIGN_EQUAL:
408 freespc = (right - left - freespc)/(double)(nobjs - 1);
409 x_pos = left;
410 break;
411 case DIA_ALIGN_ADJACENT:
412 x_pos = left;
413 break;
414 default:
415 message_warning("Wrong argument to object_list_align_h()\n");
418 dest_pos = g_new(Point, nobjs);
419 orig_pos = g_new(Point, nobjs);
421 i = 0;
422 list = objects;
423 while (list != NULL) {
424 obj = (DiaObject *) list->data;
426 switch (align) {
427 case DIA_ALIGN_LEFT:
428 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
429 break;
430 case DIA_ALIGN_CENTER:
431 pos.x = x_pos + obj->position.x - (obj->bounding_box.left + obj->bounding_box.right)/2.0;
432 break;
433 case DIA_ALIGN_RIGHT:
434 pos.x = x_pos - (obj->bounding_box.right - obj->position.x);
435 break;
436 case DIA_ALIGN_POSITION:
437 pos.x = x_pos;
438 break;
439 case DIA_ALIGN_EQUAL:
440 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
441 x_pos += obj->bounding_box.right - obj->bounding_box.left + freespc;
442 break;
443 case DIA_ALIGN_ADJACENT:
444 pos.x = x_pos + obj->position.x - obj->bounding_box.left;
445 x_pos += obj->bounding_box.right - obj->bounding_box.left;
446 break;
449 pos.y = obj->position.y;
451 orig_pos[i] = obj->position;
452 dest_pos[i] = pos;
454 obj->ops->move(obj, &pos);
456 i++;
457 list = g_list_next(list);
460 undo_move_objects(dia, orig_pos, dest_pos, g_list_copy(objects));
461 if (sort_alloc)
462 g_list_free(objects);