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 "object_ops.h"
26 #include "connectionpoint_ops.h"
27 #include "handle_ops.h"
30 #define OBJECT_CONNECT_DISTANCE 4.5
33 object_add_updates(DiaObject
*obj
, Diagram
*dia
)
38 if (obj
->highlight_color
!= NULL
) {
39 diagram_add_update_with_border(dia
, &obj
->bounding_box
, 5);
41 diagram_add_update(dia
, &obj
->bounding_box
);
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
);
57 object_add_updates_list(GList
*list
, Diagram
*dia
)
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
76 * @param snap_to_objects Whether snapping to objects should be in effect
77 * in this call (anded to the display-wide setting).
80 object_find_connectpoint_display(DDisplay
*ddisp
, Point
*pos
,
81 DiaObject
*notthis
, gboolean snap_to_objects
)
84 ConnectionPoint
*connectionpoint
;
89 diagram_find_closest_connectionpoint(ddisp
->diagram
, &connectionpoint
,
92 distance
= ddisplay_transform_length(ddisp
, distance
);
93 if (distance
< OBJECT_CONNECT_DISTANCE
) {
94 return connectionpoint
;
96 if (ddisp
->mainpoint_magnetism
&& snap_to_objects
) {
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
) {
107 for (i
= 0; i
< obj_here
->num_connections
; i
++) {
108 if (obj_here
->connections
[i
]->flags
& CP_FLAG_ANYPLACE
) {
110 return obj_here
->connections
[i
];
119 /* pushes undo info */
121 object_connect_display(DDisplay
*ddisp
, DiaObject
*obj
, Handle
*handle
,
122 gboolean snap_to_objects
)
124 ConnectionPoint
*connectionpoint
;
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
,
136 (change
->apply
)(change
, ddisp
->diagram
);
142 object_list_corner(GList
*list
)
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
);
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:
183 object_list_align_v(GList
*objects
, Diagram
*dia
, int align
)
191 real top
, bottom
, freespc
;
194 gboolean sort_alloc
= FALSE
;
199 obj
= (DiaObject
*) objects
->data
; /* First object */
201 top
= obj
->bounding_box
.top
;
202 bottom
= obj
->bounding_box
.bottom
;
203 freespc
= bottom
- top
;
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
;
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
);
230 while (list
!= NULL
) {
231 obj
= (DiaObject
*) list
->data
;
232 object_array
[i
] = obj
;
234 list
= g_list_next(list
);
236 qsort(object_array
, nobjs
, sizeof(DiaObject
*), object_list_sort_vertical
);
238 for (i
= 0; i
< nobjs
; i
++) {
239 list
= g_list_append(list
, object_array
[i
]);
242 sort_alloc
= TRUE
; /* Must remember to free the list */
243 g_free(object_array
);
247 case DIA_ALIGN_TOP
: /* TOP */
250 case DIA_ALIGN_CENTER
: /* CENTER */
251 y_pos
= (top
+ bottom
)/2.0;
253 case DIA_ALIGN_BOTTOM
: /* BOTTOM */
256 case DIA_ALIGN_POSITION
: /* OBJECT POSITION */
257 y_pos
= (top
+ bottom
)/2.0;
259 case DIA_ALIGN_EQUAL
: /* EQUAL DISTANCE */
260 freespc
= (bottom
- top
- freespc
)/(double)(nobjs
- 1);
263 case DIA_ALIGN_ADJACENT
: /* ADJACENT */
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
);
275 while (list
!= NULL
) {
276 obj
= (DiaObject
*) list
->data
;
278 pos
.x
= obj
->position
.x
;
281 case DIA_ALIGN_TOP
: /* TOP */
282 pos
.y
= y_pos
+ obj
->position
.y
- obj
->bounding_box
.top
;
284 case DIA_ALIGN_CENTER
: /* CENTER */
285 pos
.y
= y_pos
+ obj
->position
.y
- (obj
->bounding_box
.top
+ obj
->bounding_box
.bottom
)/2.0;
287 case DIA_ALIGN_BOTTOM
: /* BOTTOM */
288 pos
.y
= y_pos
- (obj
->bounding_box
.bottom
- obj
->position
.y
);
290 case DIA_ALIGN_POSITION
: /* OBJECT POSITION */
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
;
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
;
303 orig_pos
[i
] = obj
->position
;
306 obj
->ops
->move(obj
, &pos
);
309 list
= g_list_next(list
);
312 undo_move_objects(dia
, orig_pos
, dest_pos
, g_list_copy(objects
));
314 g_list_free(objects
);
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
331 object_list_align_h(GList
*objects
, Diagram
*dia
, int align
)
339 real left
, right
, freespc
= 0;
342 gboolean sort_alloc
= FALSE
;
347 obj
= (DiaObject
*) objects
->data
; /* First object */
349 left
= obj
->bounding_box
.left
;
350 right
= obj
->bounding_box
.right
;
351 freespc
= right
- left
;
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
;
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
);
378 while (list
!= NULL
) {
379 obj
= (DiaObject
*) list
->data
;
380 object_array
[i
] = obj
;
382 list
= g_list_next(list
);
384 qsort(object_array
, nobjs
, sizeof(DiaObject
*), object_list_sort_horizontal
);
386 for (i
= 0; i
< nobjs
; i
++) {
387 list
= g_list_append(list
, object_array
[i
]);
391 g_free(object_array
);
398 case DIA_ALIGN_CENTER
:
399 x_pos
= (left
+ right
)/2.0;
401 case DIA_ALIGN_RIGHT
:
404 case DIA_ALIGN_POSITION
:
405 x_pos
= (left
+ right
)/2.0;
407 case DIA_ALIGN_EQUAL
:
408 freespc
= (right
- left
- freespc
)/(double)(nobjs
- 1);
411 case DIA_ALIGN_ADJACENT
:
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
);
423 while (list
!= NULL
) {
424 obj
= (DiaObject
*) list
->data
;
428 pos
.x
= x_pos
+ obj
->position
.x
- obj
->bounding_box
.left
;
430 case DIA_ALIGN_CENTER
:
431 pos
.x
= x_pos
+ obj
->position
.x
- (obj
->bounding_box
.left
+ obj
->bounding_box
.right
)/2.0;
433 case DIA_ALIGN_RIGHT
:
434 pos
.x
= x_pos
- (obj
->bounding_box
.right
- obj
->position
.x
);
436 case DIA_ALIGN_POSITION
:
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
;
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
;
449 pos
.y
= obj
->position
.y
;
451 orig_pos
[i
] = obj
->position
;
454 obj
->ops
->move(obj
, &pos
);
457 list
= g_list_next(list
);
460 undo_move_objects(dia
, orig_pos
, dest_pos
, g_list_copy(objects
));
462 g_list_free(objects
);