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(Object
*obj
, Diagram
*dia
)
38 diagram_add_update(dia
, &obj
->bounding_box
);
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
);
53 object_add_updates_list(GList
*list
, Diagram
*dia
)
57 while (list
!= NULL
) {
58 obj
= (Object
*)list
->data
;
60 object_add_updates(obj
, dia
);
62 list
= g_list_next(list
);
67 object_find_connectpoint_display(DDisplay
*ddisp
, Point
*pos
)
70 ConnectionPoint
*connectionpoint
;
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
;
83 /* pushes undo info */
85 object_connect_display(DDisplay
*ddisp
, Object
*obj
, Handle
*handle
)
87 ConnectionPoint
*connectionpoint
;
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
,
98 (change
->apply
)(change
, ddisp
->diagram
);
104 pointer_hash(gpointer some_pointer
)
106 return (guint
) some_pointer
;
110 object_copy_list(GList
*list_orig
)
116 GHashTable
*hash_table
;
119 hash_table
= g_hash_table_new((GHashFunc
) pointer_hash
, 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: */
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
) {
146 Object
*other_obj_copy
;
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. */
156 while (other_obj
->connections
[con_point_nr
] != con_point
) {
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
);
174 object_list_corner(GList
*list
)
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
);
203 object_list_move_delta(GList
*objects
, Point
*delta
)
210 while (list
!= NULL
) {
211 obj
= (Object
*) list
->data
;
214 point_add(&pos
, delta
);
216 obj
->ops
->move(obj
, &pos
);
218 list
= g_list_next(list
);
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:
236 object_list_align_v(GList
*objects
, Diagram
*dia
, int align
)
244 real top
, bottom
, freespc
;
247 gboolean sort_alloc
= FALSE
;
252 obj
= (Object
*) objects
->data
; /* First object */
254 top
= obj
->bounding_box
.top
;
255 bottom
= obj
->bounding_box
.bottom
;
256 freespc
= bottom
- top
;
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
;
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
);
283 while (list
!= NULL
) {
284 obj
= (Object
*) list
->data
;
285 object_array
[i
] = obj
;
287 list
= g_list_next(list
);
289 qsort(object_array
, nobjs
, sizeof(Object
*), object_list_sort_vertical
);
291 for (i
= 0; i
< nobjs
; i
++) {
292 list
= g_list_append(list
, object_array
[i
]);
295 sort_alloc
= TRUE
; /* Must remember to free the list */
296 g_free(object_array
);
300 case DIA_ALIGN_TOP
: /* TOP */
303 case DIA_ALIGN_CENTER
: /* CENTER */
304 y_pos
= (top
+ bottom
)/2.0;
306 case DIA_ALIGN_BOTTOM
: /* BOTTOM */
309 case DIA_ALIGN_POSITION
: /* OBJECT POSITION */
310 y_pos
= (top
+ bottom
)/2.0;
312 case DIA_ALIGN_EQUAL
: /* EQUAL DISTANCE */
313 freespc
= (bottom
- top
- freespc
)/(double)(nobjs
- 1);
316 case DIA_ALIGN_ADJACENT
: /* ADJACENT */
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
);
328 while (list
!= NULL
) {
329 obj
= (Object
*) list
->data
;
331 pos
.x
= obj
->position
.x
;
334 case DIA_ALIGN_TOP
: /* TOP */
335 pos
.y
= y_pos
+ obj
->position
.y
- obj
->bounding_box
.top
;
337 case DIA_ALIGN_CENTER
: /* CENTER */
338 pos
.y
= y_pos
+ obj
->position
.y
- (obj
->bounding_box
.top
+ obj
->bounding_box
.bottom
)/2.0;
340 case DIA_ALIGN_BOTTOM
: /* BOTTOM */
341 pos
.y
= y_pos
- (obj
->bounding_box
.bottom
- obj
->position
.y
);
343 case DIA_ALIGN_POSITION
: /* OBJECT POSITION */
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
;
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
;
356 orig_pos
[i
] = obj
->position
;
359 obj
->ops
->move(obj
, &pos
);
362 list
= g_list_next(list
);
365 undo_move_objects(dia
, orig_pos
, dest_pos
, g_list_copy(objects
));
367 g_list_free(objects
);
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
384 object_list_align_h(GList
*objects
, Diagram
*dia
, int align
)
392 real left
, right
, freespc
= 0;
395 gboolean sort_alloc
= FALSE
;
400 obj
= (Object
*) objects
->data
; /* First object */
402 left
= obj
->bounding_box
.left
;
403 right
= obj
->bounding_box
.right
;
404 freespc
= right
- left
;
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
;
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
);
431 while (list
!= NULL
) {
432 obj
= (Object
*) list
->data
;
433 object_array
[i
] = obj
;
435 list
= g_list_next(list
);
437 qsort(object_array
, nobjs
, sizeof(Object
*), object_list_sort_horizontal
);
439 for (i
= 0; i
< nobjs
; i
++) {
440 list
= g_list_append(list
, object_array
[i
]);
444 g_free(object_array
);
451 case DIA_ALIGN_CENTER
:
452 x_pos
= (left
+ right
)/2.0;
454 case DIA_ALIGN_RIGHT
:
457 case DIA_ALIGN_POSITION
:
458 x_pos
= (left
+ right
)/2.0;
460 case DIA_ALIGN_EQUAL
:
461 freespc
= (right
- left
- freespc
)/(double)(nobjs
- 1);
464 case DIA_ALIGN_ADJACENT
:
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
);
476 while (list
!= NULL
) {
477 obj
= (Object
*) list
->data
;
481 pos
.x
= x_pos
+ obj
->position
.x
- obj
->bounding_box
.left
;
483 case DIA_ALIGN_CENTER
:
484 pos
.x
= x_pos
+ obj
->position
.x
- (obj
->bounding_box
.left
+ obj
->bounding_box
.right
)/2.0;
486 case DIA_ALIGN_RIGHT
:
487 pos
.x
= x_pos
- (obj
->bounding_box
.right
- obj
->position
.x
);
489 case DIA_ALIGN_POSITION
:
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
;
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
;
502 pos
.y
= obj
->position
.y
;
504 orig_pos
[i
] = obj
->position
;
507 obj
->ops
->move(obj
, &pos
);
510 list
= g_list_next(list
);
513 undo_move_objects(dia
, orig_pos
, dest_pos
, g_list_copy(objects
));
515 g_list_free(objects
);