2004-05-17 Hans Breuer <hans@breuer.org>
[dia.git] / app / undo.c
blobccbc4412051eabb815d284c7f989f55a547b5709
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 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 #include <config.h>
21 #include "undo.h"
22 #include "object_ops.h"
23 #include "properties.h"
24 #include "connectionpoint_ops.h"
25 #include "focus.h"
26 #include "group.h"
27 #include "preferences.h"
28 #include "diagram_tree_window.h"
30 #if 0
31 #define DEBUG_PRINTF(args) printf args
32 #else
33 #define DEBUG_PRINTF(args)
34 #endif
36 static void
37 transaction_point_pointer(Change *change, Diagram *dia)
39 /* Empty function used to track transactionpoints. */
42 static int
43 is_transactionpoint(Change *change)
45 return change->apply == transaction_point_pointer;
48 static Change *
49 new_transactionpoint(void)
51 Change *transaction = g_new(Change, 1);
53 if (transaction) {
54 transaction->apply = transaction_point_pointer;
55 transaction->revert = transaction_point_pointer;
56 transaction->free = NULL;
59 return transaction;
62 UndoStack *
63 new_undo_stack(Diagram *dia)
65 UndoStack *stack;
66 Change *transaction;
68 stack = g_new(UndoStack, 1);
69 if (stack!=NULL){
70 stack->dia = dia;
71 transaction = new_transactionpoint();
72 transaction->next = transaction->prev = NULL;
73 stack->last_change = transaction;
74 stack->current_change = transaction;
75 stack->last_save = transaction;
76 stack->depth = 0;
78 return stack;
81 void
82 undo_destroy(UndoStack *stack)
84 undo_clear(stack);
85 g_free(stack->current_change); /* Free first transaction point. */
86 g_free(stack);
91 static void
92 undo_remove_redo_info(UndoStack *stack)
94 Change *change;
95 Change *next_change;
97 DEBUG_PRINTF(("UNDO: Removing redo info\n"));
99 change = stack->current_change->next;
100 stack->current_change->next = NULL;
101 stack->last_change = stack->current_change;
103 while (change != NULL) {
104 next_change = change->next;
105 if (change->free)
106 (change->free)(change);
107 g_free(change);
108 change = next_change;
112 static int
113 depth(UndoStack *stack)
115 int i;
116 Change *change;
117 change = stack->current_change;
119 i = 0;
120 while (change->prev != NULL) {
121 change = change->prev;
122 i++;
124 return i;
127 void
128 undo_push_change(UndoStack *stack, Change *change)
130 if (stack->current_change != stack->last_change)
131 undo_remove_redo_info(stack);
133 DEBUG_PRINTF(("UNDO: Push new change at %d\n", depth(stack)));
135 change->prev = stack->last_change;
136 change->next = NULL;
137 stack->last_change->next = change;
138 stack->last_change = change;
139 stack->current_change = change;
142 static void
143 undo_delete_lowest_transaction(UndoStack *stack)
145 Change *change;
146 Change *next_change;
148 /* Find the lowest change: */
149 change = stack->current_change;
150 while (change->prev != NULL) {
151 change = change->prev;
154 /* Remove changes from the bottom until (and including)
155 * the first transactionpoint.
156 * Stop if we reach current_change or NULL.
158 do {
159 if ( (change == NULL) || (change == stack->current_change))
160 break;
162 next_change = change->next;
163 DEBUG_PRINTF(("freeing one change from the bottom.\n"));
164 if (change->free)
165 (change->free)(change);
166 g_free(change);
167 change = next_change;
168 } while (!is_transactionpoint(change));
170 if (is_transactionpoint(change)) {
171 stack->depth--;
172 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
174 change->prev = NULL;
177 void
178 undo_set_transactionpoint(UndoStack *stack)
180 Change *transaction;
182 if (is_transactionpoint(stack->current_change))
183 return;
185 DEBUG_PRINTF(("UNDO: Push new transactionpoint at %d\n", depth(stack)));
187 transaction = new_transactionpoint();
189 undo_push_change(stack, transaction);
190 stack->depth++;
191 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
193 if (prefs.undo_depth > 0) {
194 while (stack->depth > prefs.undo_depth){
195 undo_delete_lowest_transaction(stack);
200 void
201 undo_revert_to_last_tp(UndoStack *stack)
203 Change *change;
204 Change *prev_change;
206 if (stack->current_change->prev == NULL)
207 return; /* Can't revert first transactionpoint */
209 change = stack->current_change;
210 do {
211 prev_change = change->prev;
212 (change->revert)(change, stack->dia);
213 change = prev_change;
214 } while (!is_transactionpoint(change));
215 stack->current_change = change;
216 stack->depth--;
217 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
220 void
221 undo_apply_to_next_tp(UndoStack *stack)
223 Change *change;
224 Change *next_change;
226 change = stack->current_change;
228 if (change->next == NULL)
229 return /* Already at top. */;
231 do {
232 next_change = change->next;
233 (change->apply)(change, stack->dia);
234 change = next_change;
235 } while ( (change != NULL) &&
236 (!is_transactionpoint(change)) );
237 if (change == NULL)
238 change = stack->last_change;
239 stack->current_change = change;
240 stack->depth++;
241 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
245 void
246 undo_clear(UndoStack *stack)
248 Change *change;
250 DEBUG_PRINTF(("undo_clear()\n"));
252 change = stack->current_change;
254 while (change->prev != NULL) {
255 change = change->prev;
258 stack->current_change = change;
259 stack->depth = 0;
260 undo_remove_redo_info(stack);
263 /** Marks the undo stack at the time of a save.
265 void
266 undo_mark_save(UndoStack *stack)
268 stack->last_save = stack->current_change;
271 /** Returns true if the diagram is undo-wise in the same state as at
272 * last save, i.e. the current change is the same as it was at last save.
274 gboolean
275 undo_is_saved(UndoStack *stack)
277 return stack->last_save == stack->current_change;
282 /****************************************************************/
283 /****************************************************************/
284 /***************** *********************/
285 /***************** Specific undo functions: *********************/
286 /***************** *********************/
287 /****************************************************************/
288 /****************************************************************/
290 /******** Move object list: */
292 struct MoveObjectsChange {
293 Change change;
295 Point *orig_pos;
296 Point *dest_pos;
297 GList *obj_list;
300 static void
301 move_objects_apply(struct MoveObjectsChange *change, Diagram *dia)
303 GList *list;
304 int i;
305 Object *obj;
307 object_add_updates_list(change->obj_list, dia);
309 list = change->obj_list;
310 i=0;
311 while (list != NULL) {
312 obj = (Object *) list->data;
314 obj->ops->move(obj, &change->dest_pos[i]);
316 list = g_list_next(list); i++;
319 list = change->obj_list;
320 while (list!=NULL) {
321 obj = (Object *) list->data;
323 diagram_update_connections_object(dia, obj, TRUE);
325 list = g_list_next(list);
328 object_add_updates_list(change->obj_list, dia);
331 static void
332 move_objects_revert(struct MoveObjectsChange *change, Diagram *dia)
334 GList *list;
335 int i;
336 Object *obj;
338 object_add_updates_list(change->obj_list, dia);
340 list = change->obj_list;
341 i=0;
342 while (list != NULL) {
343 obj = (Object *) list->data;
345 obj->ops->move(obj, &change->orig_pos[i]);
347 list = g_list_next(list); i++;
350 list = change->obj_list;
351 while (list!=NULL) {
352 obj = (Object *) list->data;
354 diagram_update_connections_object(dia, obj, TRUE);
356 list = g_list_next(list);
359 object_add_updates_list(change->obj_list, dia);
362 static void
363 move_objects_free(struct MoveObjectsChange *change)
365 g_free(change->orig_pos);
366 g_free(change->dest_pos);
367 g_list_free(change->obj_list);
370 extern Change *
371 undo_move_objects(Diagram *dia, Point *orig_pos, Point *dest_pos,
372 GList *obj_list)
374 struct MoveObjectsChange *change;
376 change = g_new(struct MoveObjectsChange, 1);
378 change->change.apply = (UndoApplyFunc) move_objects_apply;
379 change->change.revert = (UndoRevertFunc) move_objects_revert;
380 change->change.free = (UndoFreeFunc) move_objects_free;
382 change->orig_pos = orig_pos;
383 change->dest_pos = dest_pos;
384 change->obj_list = obj_list;
386 DEBUG_PRINTF(("UNDO: Push new move objects at %d\n", depth(dia->undo)));
387 undo_push_change(dia->undo, (Change *) change);
388 return (Change *)change;
391 /********** Move handle: */
393 struct MoveHandleChange {
394 Change change;
396 Point orig_pos;
397 Point dest_pos;
398 Handle *handle;
399 Object *obj;
402 static void
403 move_handle_apply(struct MoveHandleChange *change, Diagram *dia)
405 object_add_updates(change->obj, dia);
406 change->obj->ops->move_handle(change->obj, change->handle,
407 &change->dest_pos, NULL,
408 HANDLE_MOVE_USER_FINAL, 0);
409 object_add_updates(change->obj, dia);
410 diagram_update_connections_object(dia, change->obj, TRUE);
413 static void
414 move_handle_revert(struct MoveHandleChange *change, Diagram *dia)
416 object_add_updates(change->obj, dia);
417 change->obj->ops->move_handle(change->obj, change->handle,
418 &change->orig_pos, NULL,
419 HANDLE_MOVE_USER_FINAL, 0);
420 object_add_updates(change->obj, dia);
421 diagram_update_connections_object(dia, change->obj, TRUE);
424 static void
425 move_handle_free(struct MoveHandleChange *change)
430 Change *
431 undo_move_handle(Diagram *dia,
432 Handle *handle, Object *obj,
433 Point orig_pos, Point dest_pos)
435 struct MoveHandleChange *change;
437 change = g_new(struct MoveHandleChange, 1);
439 change->change.apply = (UndoApplyFunc) move_handle_apply;
440 change->change.revert = (UndoRevertFunc) move_handle_revert;
441 change->change.free = (UndoFreeFunc) move_handle_free;
443 change->orig_pos = orig_pos;
444 change->dest_pos = dest_pos;
445 change->handle = handle;
446 change->obj = obj;
448 DEBUG_PRINTF(("UNDO: Push new move handle at %d\n", depth(dia->undo)));
450 undo_push_change(dia->undo, (Change *) change);
451 return (Change *)change;
454 /***************** Connect object: */
456 struct ConnectChange {
457 Change change;
459 Object *obj;
460 Handle *handle;
461 ConnectionPoint *connectionpoint;
462 Point handle_pos;
465 static void
466 connect_apply(struct ConnectChange *change, Diagram *dia)
468 object_connect(change->obj, change->handle, change->connectionpoint);
470 object_add_updates(change->obj, dia);
471 change->obj->ops->move_handle(change->obj, change->handle ,
472 &change->connectionpoint->pos,
473 change->connectionpoint,
474 HANDLE_MOVE_CONNECTED, 0);
476 object_add_updates(change->obj, dia);
479 static void
480 connect_revert(struct ConnectChange *change, Diagram *dia)
482 object_unconnect(change->obj, change->handle);
484 object_add_updates(change->obj, dia);
485 change->obj->ops->move_handle(change->obj, change->handle ,
486 &change->handle_pos, NULL,
487 HANDLE_MOVE_CONNECTED, 0);
489 object_add_updates(change->obj, dia);
492 static void
493 connect_free(struct ConnectChange *change)
497 extern Change *
498 undo_connect(Diagram *dia, Object *obj, Handle *handle,
499 ConnectionPoint *connectionpoint)
501 struct ConnectChange *change;
503 change = g_new(struct ConnectChange, 1);
505 change->change.apply = (UndoApplyFunc) connect_apply;
506 change->change.revert = (UndoRevertFunc) connect_revert;
507 change->change.free = (UndoFreeFunc) connect_free;
509 change->obj = obj;
510 change->handle = handle;
511 change->handle_pos = handle->pos;
512 change->connectionpoint = connectionpoint;
514 DEBUG_PRINTF(("UNDO: Push new connect at %d\n", depth(dia->undo)));
515 undo_push_change(dia->undo, (Change *) change);
516 return (Change *)change;
519 /*************** Unconnect object: */
521 struct UnconnectChange {
522 Change change;
524 Object *obj;
525 Handle *handle;
526 ConnectionPoint *connectionpoint;
529 static void
530 unconnect_apply(struct UnconnectChange *change, Diagram *dia)
532 object_unconnect(change->obj, change->handle);
534 object_add_updates(change->obj, dia);
537 static void
538 unconnect_revert(struct UnconnectChange *change, Diagram *dia)
540 object_connect(change->obj, change->handle, change->connectionpoint);
542 object_add_updates(change->obj, dia);
545 static void
546 unconnect_free(struct UnconnectChange *change)
550 extern Change *
551 undo_unconnect(Diagram *dia, Object *obj, Handle *handle)
553 struct UnconnectChange *change;
555 change = g_new(struct UnconnectChange, 1);
557 change->change.apply = (UndoApplyFunc) unconnect_apply;
558 change->change.revert = (UndoRevertFunc) unconnect_revert;
559 change->change.free = (UndoFreeFunc) unconnect_free;
561 change->obj = obj;
562 change->handle = handle;
563 change->connectionpoint = handle->connected_to;
565 DEBUG_PRINTF(("UNDO: Push new unconnect at %d\n", depth(dia->undo)));
566 undo_push_change(dia->undo, (Change *) change);
567 return (Change *)change;
571 /******** Delete object list: */
573 struct DeleteObjectsChange {
574 Change change;
576 Layer *layer;
577 GList *obj_list; /* Owning reference when applied */
578 GList *original_objects;
579 int applied;
582 static void
583 delete_objects_apply(struct DeleteObjectsChange *change, Diagram *dia)
585 GList *list;
587 DEBUG_PRINTF(("delete_objects_apply()\n"));
588 change->applied = 1;
589 diagram_unselect_objects(dia, change->obj_list);
590 layer_remove_objects(change->layer, change->obj_list);
591 object_add_updates_list(change->obj_list, dia);
593 list = change->obj_list;
594 while (list != NULL) {
595 Object *obj = (Object *)list->data;
597 /* Have to hide any open properties dialog
598 if it contains some object in cut_list */
599 properties_hide_if_shown(dia, obj);
601 /* Remove focus if active */
602 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
603 remove_focus();
606 if (obj->parent) /* Lose references to deleted object */
607 obj->parent->children = g_list_remove(obj->parent->children, obj);
609 list = g_list_next(list);
612 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
615 static void
616 delete_objects_revert(struct DeleteObjectsChange *change, Diagram *dia)
618 GList *list;
619 DEBUG_PRINTF(("delete_objects_revert()\n"));
620 change->applied = 0;
621 layer_set_object_list(change->layer,
622 g_list_copy(change->original_objects));
623 object_add_updates_list(change->obj_list, dia);
625 list = change->obj_list;
626 while (list)
628 Object *obj = (Object *) list->data;
629 if (obj->parent) /* Restore child references */
630 obj->parent->children = g_list_append(obj->parent->children, obj);
632 list = g_list_next(list);
635 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
638 static void
639 delete_objects_free(struct DeleteObjectsChange *change)
641 DEBUG_PRINTF(("delete_objects_free()\n"));
642 if (change->applied)
643 destroy_object_list(change->obj_list);
644 else
645 g_list_free(change->obj_list);
646 g_list_free(change->original_objects);
650 This function deletes specified objects along with any children
651 they might have.
652 undo_delete_objects() only deletes the objects that are specified.
654 Change *
655 undo_delete_objects_children(Diagram *dia, GList *obj_list)
657 return undo_delete_objects(dia, parent_list_affected(obj_list));
660 Change *
661 undo_delete_objects(Diagram *dia, GList *obj_list)
663 struct DeleteObjectsChange *change;
665 change = g_new(struct DeleteObjectsChange, 1);
667 change->change.apply = (UndoApplyFunc) delete_objects_apply;
668 change->change.revert = (UndoRevertFunc) delete_objects_revert;
669 change->change.free = (UndoFreeFunc) delete_objects_free;
671 change->layer = dia->data->active_layer;
672 change->obj_list = obj_list;
673 change->original_objects = g_list_copy(dia->data->active_layer->objects);
674 change->applied = 0;
676 DEBUG_PRINTF(("UNDO: Push new delete objects at %d\n", depth(dia->undo)));
677 undo_push_change(dia->undo, (Change *) change);
678 return (Change *)change;
681 /******** Insert object list: */
683 struct InsertObjectsChange {
684 Change change;
686 Layer *layer;
687 GList *obj_list; /* Owning reference when not applied */
688 int applied;
691 static void
692 insert_objects_apply(struct InsertObjectsChange *change, Diagram *dia)
694 DEBUG_PRINTF(("insert_objects_apply()\n"));
695 change->applied = 1;
696 layer_add_objects(change->layer, g_list_copy(change->obj_list));
697 object_add_updates_list(change->obj_list, dia);
698 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
701 static void
702 insert_objects_revert(struct InsertObjectsChange *change, Diagram *dia)
704 GList *list;
706 DEBUG_PRINTF(("insert_objects_revert()\n"));
707 change->applied = 0;
708 diagram_unselect_objects(dia, change->obj_list);
709 layer_remove_objects(change->layer, change->obj_list);
710 object_add_updates_list(change->obj_list, dia);
712 list = change->obj_list;
713 while (list != NULL) {
714 Object *obj = (Object *)list->data;
716 /* Have to hide any open properties dialog
717 if it contains some object in cut_list */
718 properties_hide_if_shown(dia, obj);
720 /* Remove focus if active */
721 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
722 remove_focus();
725 list = g_list_next(list);
728 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
731 static void
732 insert_objects_free(struct InsertObjectsChange *change)
734 DEBUG_PRINTF(("insert_objects_free()\n"));
735 if (!change->applied)
736 destroy_object_list(change->obj_list);
737 else
738 g_list_free(change->obj_list);
741 Change *
742 undo_insert_objects(Diagram *dia, GList *obj_list, int applied)
744 struct InsertObjectsChange *change;
746 change = g_new(struct InsertObjectsChange, 1);
748 change->change.apply = (UndoApplyFunc) insert_objects_apply;
749 change->change.revert = (UndoRevertFunc) insert_objects_revert;
750 change->change.free = (UndoFreeFunc) insert_objects_free;
752 change->layer = dia->data->active_layer;
753 change->obj_list = obj_list;
754 change->applied = applied;
756 DEBUG_PRINTF(("UNDO: Push new insert objects at %d\n", depth(dia->undo)));
757 undo_push_change(dia->undo, (Change *) change);
758 return (Change *)change;
761 /******** Reorder object list: */
763 struct ReorderObjectsChange {
764 Change change;
766 Layer *layer;
767 GList *changed_list; /* Owning reference when applied */
768 GList *original_objects;
769 GList *reordered_objects;
772 static void
773 reorder_objects_apply(struct ReorderObjectsChange *change, Diagram *dia)
775 DEBUG_PRINTF(("reorder_objects_apply()\n"));
776 layer_set_object_list(change->layer,
777 g_list_copy(change->reordered_objects));
778 object_add_updates_list(change->changed_list, dia);
781 static void
782 reorder_objects_revert(struct ReorderObjectsChange *change, Diagram *dia)
784 DEBUG_PRINTF(("reorder_objects_revert()\n"));
785 layer_set_object_list(change->layer,
786 g_list_copy(change->original_objects));
787 object_add_updates_list(change->changed_list, dia);
790 static void
791 reorder_objects_free(struct ReorderObjectsChange *change)
793 DEBUG_PRINTF(("reorder_objects_free()\n"));
794 g_list_free(change->changed_list);
795 g_list_free(change->original_objects);
796 g_list_free(change->reordered_objects);
799 Change *
800 undo_reorder_objects(Diagram *dia, GList *changed_list, GList *orig_list)
802 struct ReorderObjectsChange *change;
804 change = g_new(struct ReorderObjectsChange, 1);
806 change->change.apply = (UndoApplyFunc) reorder_objects_apply;
807 change->change.revert = (UndoRevertFunc) reorder_objects_revert;
808 change->change.free = (UndoFreeFunc) reorder_objects_free;
810 change->layer = dia->data->active_layer;
811 change->changed_list = changed_list;
812 change->original_objects = orig_list;
813 change->reordered_objects = g_list_copy(dia->data->active_layer->objects);
815 DEBUG_PRINTF(("UNDO: Push new reorder objects at %d\n", depth(dia->undo)));
816 undo_push_change(dia->undo, (Change *) change);
817 return (Change *)change;
820 /******** ObjectChange: */
822 struct ObjectChangeChange {
823 Change change;
825 Object *obj;
826 ObjectChange *obj_change;
830 static void
831 object_change_apply(struct ObjectChangeChange *change,
832 Diagram *dia)
834 object_add_updates(change->obj, dia);
835 change->obj_change->apply(change->obj_change, change->obj);
836 { /* Make sure object updates its data: */
837 Point p = change->obj->position;
838 (change->obj->ops->move)(change->obj,&p);
840 object_add_updates(change->obj, dia);
842 diagram_update_connections_object(dia, change->obj, TRUE);
844 properties_update_if_shown(dia, change->obj);
847 static void
848 object_change_revert(struct ObjectChangeChange *change,
849 Diagram *dia)
851 object_add_updates(change->obj, dia);
852 change->obj_change->revert(change->obj_change, change->obj);
853 { /* Make sure object updates its data: */
854 Point p = change->obj->position;
855 (change->obj->ops->move)(change->obj,&p);
857 object_add_updates(change->obj, dia);
859 diagram_update_connections_object(dia, change->obj, TRUE);
861 properties_update_if_shown(dia, change->obj);
864 static void
865 object_change_free(struct ObjectChangeChange *change)
867 DEBUG_PRINTF(("state_change_free()\n"));
868 if (change->obj_change->free)
869 (*change->obj_change->free)(change->obj_change);
870 g_free(change->obj_change);
873 Change *
874 undo_object_change(Diagram *dia, Object *obj,
875 ObjectChange *obj_change)
877 struct ObjectChangeChange *change;
879 change = g_new(struct ObjectChangeChange, 1);
881 change->change.apply = (UndoApplyFunc) object_change_apply;
882 change->change.revert = (UndoRevertFunc) object_change_revert;
883 change->change.free = (UndoFreeFunc) object_change_free;
885 change->obj = obj;
886 change->obj_change = obj_change;
888 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
889 undo_push_change(dia->undo, (Change *) change);
890 return (Change *)change;
893 /******** Group object list: */
895 /** Grouping and ungrouping are two subtly different changes: While
896 * ungrouping preserves the front/back position, grouping cannot do that,
897 * since the act of grouping destroys positions for the members of the
898 * group, and those positions have to be restored in the undo.
901 struct GroupObjectsChange {
902 Change change;
904 Layer *layer;
905 Object *group; /* owning reference if not applied */
906 GList *obj_list; /* The list of objects in this group. Owned by the
907 group */
908 GList *orig_list; /* A copy of the original list of all objects,
909 from before the group was created. Owned by
910 the group */
911 int applied;
914 static void
915 group_objects_apply(struct GroupObjectsChange *change, Diagram *dia)
917 GList *list;
919 DEBUG_PRINTF(("group_objects_apply()\n"));
921 change->applied = 1;
923 diagram_unselect_objects(dia, change->obj_list);
924 layer_remove_objects(change->layer, change->obj_list);
925 layer_add_object(change->layer, change->group);
926 object_add_updates(change->group, dia);
928 list = change->obj_list;
929 while (list != NULL) {
930 Object *obj = (Object *)list->data;
932 /* Have to hide any open properties dialog
933 if it contains some object in cut_list */
934 properties_hide_if_shown(dia, obj);
936 /* Remove focus if active */
937 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
938 remove_focus();
941 object_add_updates(obj, dia);
943 list = g_list_next(list);
946 diagram_tree_add_object(diagram_tree(), dia, change->group);
947 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
950 static void
951 group_objects_revert(struct GroupObjectsChange *change, Diagram *dia)
953 GList *old_list;
955 DEBUG_PRINTF(("group_objects_revert()\n"));
956 change->applied = 0;
958 diagram_unselect_object(dia, change->group);
959 object_add_updates(change->group, dia);
961 layer_set_object_list(change->layer, g_list_copy(change->orig_list));
963 object_add_updates_list(change->obj_list, dia);
965 properties_hide_if_shown(dia, change->group);
967 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
968 diagram_tree_remove_object(diagram_tree(), change->group);
971 static void
972 group_objects_free(struct GroupObjectsChange *change)
974 DEBUG_PRINTF(("group_objects_free()\n"));
975 if (!change->applied) {
976 group_destroy_shallow(change->group);
977 change->group = NULL;
978 change->obj_list = NULL;
979 /** Leak here? */
981 g_list_free(change->orig_list);
984 Change *
985 undo_group_objects(Diagram *dia, GList *obj_list, Object *group,
986 GList *orig_list)
988 struct GroupObjectsChange *change;
990 change = g_new(struct GroupObjectsChange, 1);
992 change->change.apply = (UndoApplyFunc) group_objects_apply;
993 change->change.revert = (UndoRevertFunc) group_objects_revert;
994 change->change.free = (UndoFreeFunc) group_objects_free;
996 change->layer = dia->data->active_layer;
997 change->group = group;
998 change->obj_list = obj_list;
999 change->orig_list = orig_list;
1000 change->applied = 1;
1002 DEBUG_PRINTF(("UNDO: Push new group objects at %d\n", depth(dia->undo)));
1003 undo_push_change(dia->undo, (Change *) change);
1004 return (Change *)change;
1007 /******** Ungroup object list: */
1009 struct UngroupObjectsChange {
1010 Change change;
1012 Layer *layer;
1013 Object *group; /* owning reference if applied */
1014 GList *obj_list; /* This list is owned by the ungroup. */
1015 int group_index;
1016 int applied;
1019 static void
1020 ungroup_objects_apply(struct UngroupObjectsChange *change, Diagram *dia)
1022 DEBUG_PRINTF(("ungroup_objects_apply()\n"));
1024 change->applied = 1;
1026 diagram_unselect_object(dia, change->group);
1027 object_add_updates(change->group, dia);
1028 layer_replace_object_with_list(change->layer, change->group,
1029 g_list_copy(change->obj_list));
1030 object_add_updates_list(change->obj_list, dia);
1032 properties_hide_if_shown(dia, change->group);
1034 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
1035 diagram_tree_remove_object(diagram_tree(), change->group);
1038 static void
1039 ungroup_objects_revert(struct UngroupObjectsChange *change, Diagram *dia)
1041 GList *list;
1043 DEBUG_PRINTF(("ungroup_objects_revert()\n"));
1044 change->applied = 0;
1047 diagram_unselect_objects(dia, change->obj_list);
1048 layer_remove_objects(change->layer, change->obj_list);
1049 layer_add_object_at(change->layer, change->group, change->group_index);
1050 object_add_updates(change->group, dia);
1052 list = change->obj_list;
1053 while (list != NULL) {
1054 Object *obj = (Object *)list->data;
1056 /* Have to hide any open properties dialog
1057 if it contains some object in cut_list */
1058 properties_hide_if_shown(dia, obj);
1060 /* Remove focus if active */
1061 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
1062 remove_focus();
1065 object_add_updates(obj, dia);
1067 list = g_list_next(list);
1070 diagram_tree_add_object(diagram_tree(), dia, change->group);
1071 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
1074 static void
1075 ungroup_objects_free(struct UngroupObjectsChange *change)
1077 DEBUG_PRINTF(("ungroup_objects_free()\n"));
1078 if (change->applied) {
1079 group_destroy_shallow(change->group);
1080 change->group = NULL;
1081 change->obj_list = NULL;
1085 Change *
1086 undo_ungroup_objects(Diagram *dia, GList *obj_list, Object *group,
1087 int group_index)
1089 struct UngroupObjectsChange *change;
1091 change = g_new(struct UngroupObjectsChange, 1);
1093 change->change.apply = (UndoApplyFunc) ungroup_objects_apply;
1094 change->change.revert = (UndoRevertFunc) ungroup_objects_revert;
1095 change->change.free = (UndoFreeFunc) ungroup_objects_free;
1097 change->layer = dia->data->active_layer;
1098 change->group = group;
1099 change->obj_list = obj_list;
1100 change->group_index = group_index;
1101 change->applied = 1;
1103 DEBUG_PRINTF(("UNDO: Push new ungroup objects at %d\n", depth(dia->undo)));
1104 undo_push_change(dia->undo, (Change *) change);
1105 return (Change *)change;
1108 /******* PARENTING */
1110 struct ParentChange {
1111 Change change;
1112 Object *parentobj, *childobj;
1113 gboolean parent;
1116 /** Performs the actual parenting of a child to a parent.
1117 * Since no display changes arise from this, we need call no update. */
1118 static void
1119 parent_object(Diagram *dia, Object *parent, Object *child)
1121 child->parent = parent;
1122 parent->children = g_list_prepend(parent->children, child);
1125 /** Performs the actual removal of a child from a parent.
1126 * Since no display changes arise from this, we need call no update. */
1127 static void
1128 unparent_object(Diagram *dia, Object *parent, Object *child)
1130 child->parent = NULL;
1131 parent->children = g_list_remove(parent->children, child);
1134 /** Applies the given ParentChange */
1135 static void
1136 parent_change_apply(Change *change, Diagram *dia)
1138 struct ParentChange *parentchange = (struct ParentChange*)change;
1139 if (parentchange->parent) {
1140 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1141 } else {
1142 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1146 /** Reverts the given ParentChange */
1147 static void
1148 parent_change_revert(Change *change, Diagram *dia)
1150 struct ParentChange *parentchange = (struct ParentChange*)change;
1151 if (!parentchange->parent) {
1152 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1153 } else {
1154 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1158 /** Frees items in the change -- none really */
1159 static void
1160 parent_change_free(Change *change)
1164 /** Create a new Change object for parenting/unparenting.
1165 * `parent' is TRUE if applying this change makes childobj a
1166 * child of parentobj.
1168 Change *
1169 undo_parenting(Diagram *dia, Object* parentobj, Object* childobj,
1170 gboolean parent)
1172 struct ParentChange *parentchange = g_new0(struct ParentChange, 1);
1173 Change *change = (Change*)parentchange;
1174 change->apply = parent_change_apply;
1175 change->revert = parent_change_revert;
1176 change->free = parent_change_free;
1178 parentchange->parentobj = parentobj;
1179 parentchange->childobj = childobj;
1180 parentchange->parent = parent;
1182 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
1183 undo_push_change(dia->undo, change);
1184 return change;