Correct removal of dynamic objects, psft2 renderer using scaled layout.
[dia.git] / app / undo.c
blob9b07fb195b006080c1d7ecec2612fc2f78a6a637
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->depth = 0;
77 return stack;
80 void
81 undo_destroy(UndoStack *stack)
83 undo_clear(stack);
84 g_free(stack->current_change); /* Free first transaction point. */
85 g_free(stack);
90 static void
91 undo_remove_redo_info(UndoStack *stack)
93 Change *change;
94 Change *next_change;
96 DEBUG_PRINTF(("UNDO: Removing redo info\n"));
98 change = stack->current_change->next;
99 stack->current_change->next = NULL;
100 stack->last_change = stack->current_change;
102 while (change != NULL) {
103 next_change = change->next;
104 if (change->free)
105 (change->free)(change);
106 g_free(change);
107 change = next_change;
111 static int
112 depth(UndoStack *stack)
114 int i;
115 Change *change;
116 change = stack->current_change;
118 i = 0;
119 while (change->prev != NULL) {
120 change = change->prev;
121 i++;
123 return i;
126 void
127 undo_push_change(UndoStack *stack, Change *change)
129 if (stack->current_change != stack->last_change)
130 undo_remove_redo_info(stack);
132 DEBUG_PRINTF(("UNDO: Push new change at %d\n", depth(stack)));
134 change->prev = stack->last_change;
135 change->next = NULL;
136 stack->last_change->next = change;
137 stack->last_change = change;
138 stack->current_change = change;
141 static void
142 undo_delete_lowest_transaction(UndoStack *stack)
144 Change *change;
145 Change *next_change;
147 /* Find the lowest change: */
148 change = stack->current_change;
149 while (change->prev != NULL) {
150 change = change->prev;
153 /* Remove changes from the bottom until (and including)
154 * the first transactionpoint.
155 * Stop if we reach current_change or NULL.
157 do {
158 if ( (change == NULL) || (change == stack->current_change))
159 break;
161 next_change = change->next;
162 DEBUG_PRINTF(("freeing one change from the bottom.\n"));
163 if (change->free)
164 (change->free)(change);
165 g_free(change);
166 change = next_change;
167 } while (!is_transactionpoint(change));
169 if (is_transactionpoint(change)) {
170 stack->depth--;
171 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
173 change->prev = NULL;
176 void
177 undo_set_transactionpoint(UndoStack *stack)
179 Change *transaction;
181 if (is_transactionpoint(stack->current_change))
182 return;
184 DEBUG_PRINTF(("UNDO: Push new transactionpoint at %d\n", depth(stack)));
186 transaction = new_transactionpoint();
188 undo_push_change(stack, transaction);
189 stack->depth++;
190 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
192 if (prefs.undo_depth > 0) {
193 while (stack->depth > prefs.undo_depth){
194 undo_delete_lowest_transaction(stack);
199 void
200 undo_revert_to_last_tp(UndoStack *stack)
202 Change *change;
203 Change *prev_change;
205 if (stack->current_change->prev == NULL)
206 return; /* Can't revert first transactionpoint */
208 change = stack->current_change;
209 do {
210 prev_change = change->prev;
211 (change->revert)(change, stack->dia);
212 change = prev_change;
213 } while (!is_transactionpoint(change));
214 stack->current_change = change;
215 stack->depth--;
216 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
219 void
220 undo_apply_to_next_tp(UndoStack *stack)
222 Change *change;
223 Change *next_change;
225 change = stack->current_change;
227 if (change->next == NULL)
228 return /* Already at top. */;
230 do {
231 next_change = change->next;
232 (change->apply)(change, stack->dia);
233 change = next_change;
234 } while ( (change != NULL) &&
235 (!is_transactionpoint(change)) );
236 if (change == NULL)
237 change = stack->last_change;
238 stack->current_change = change;
239 stack->depth++;
240 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
244 void
245 undo_clear(UndoStack *stack)
247 Change *change;
249 DEBUG_PRINTF(("undo_clear()\n"));
251 change = stack->current_change;
253 while (change->prev != NULL) {
254 change = change->prev;
257 stack->current_change = change;
258 stack->depth = 0;
259 undo_remove_redo_info(stack);
263 /****************************************************************/
264 /****************************************************************/
265 /***************** *********************/
266 /***************** Specific undo functions: *********************/
267 /***************** *********************/
268 /****************************************************************/
269 /****************************************************************/
271 /******** Move object list: */
273 struct MoveObjectsChange {
274 Change change;
276 Point *orig_pos;
277 Point *dest_pos;
278 GList *obj_list;
281 static void
282 move_objects_apply(struct MoveObjectsChange *change, Diagram *dia)
284 GList *list;
285 int i;
286 Object *obj;
288 object_add_updates_list(change->obj_list, dia);
290 list = change->obj_list;
291 i=0;
292 while (list != NULL) {
293 obj = (Object *) list->data;
295 obj->ops->move(obj, &change->dest_pos[i]);
297 list = g_list_next(list); i++;
300 list = change->obj_list;
301 while (list!=NULL) {
302 obj = (Object *) list->data;
304 diagram_update_connections_object(dia, obj, TRUE);
306 list = g_list_next(list);
309 object_add_updates_list(change->obj_list, dia);
312 static void
313 move_objects_revert(struct MoveObjectsChange *change, Diagram *dia)
315 GList *list;
316 int i;
317 Object *obj;
319 object_add_updates_list(change->obj_list, dia);
321 list = change->obj_list;
322 i=0;
323 while (list != NULL) {
324 obj = (Object *) list->data;
326 obj->ops->move(obj, &change->orig_pos[i]);
328 list = g_list_next(list); i++;
331 list = change->obj_list;
332 while (list!=NULL) {
333 obj = (Object *) list->data;
335 diagram_update_connections_object(dia, obj, TRUE);
337 list = g_list_next(list);
340 object_add_updates_list(change->obj_list, dia);
343 static void
344 move_objects_free(struct MoveObjectsChange *change)
346 g_free(change->orig_pos);
347 g_free(change->dest_pos);
348 g_list_free(change->obj_list);
351 extern Change *
352 undo_move_objects(Diagram *dia, Point *orig_pos, Point *dest_pos,
353 GList *obj_list)
355 struct MoveObjectsChange *change;
357 change = g_new(struct MoveObjectsChange, 1);
359 change->change.apply = (UndoApplyFunc) move_objects_apply;
360 change->change.revert = (UndoRevertFunc) move_objects_revert;
361 change->change.free = (UndoFreeFunc) move_objects_free;
363 change->orig_pos = orig_pos;
364 change->dest_pos = dest_pos;
365 change->obj_list = obj_list;
367 DEBUG_PRINTF(("UNDO: Push new move objects at %d\n", depth(dia->undo)));
368 undo_push_change(dia->undo, (Change *) change);
369 return (Change *)change;
372 /********** Move handle: */
374 struct MoveHandleChange {
375 Change change;
377 Point orig_pos;
378 Point dest_pos;
379 Handle *handle;
380 Object *obj;
383 static void
384 move_handle_apply(struct MoveHandleChange *change, Diagram *dia)
386 object_add_updates(change->obj, dia);
387 change->obj->ops->move_handle(change->obj, change->handle,
388 &change->dest_pos, NULL,
389 HANDLE_MOVE_USER_FINAL, 0);
390 object_add_updates(change->obj, dia);
391 diagram_update_connections_object(dia, change->obj, TRUE);
394 static void
395 move_handle_revert(struct MoveHandleChange *change, Diagram *dia)
397 object_add_updates(change->obj, dia);
398 change->obj->ops->move_handle(change->obj, change->handle,
399 &change->orig_pos, NULL,
400 HANDLE_MOVE_USER_FINAL, 0);
401 object_add_updates(change->obj, dia);
402 diagram_update_connections_object(dia, change->obj, TRUE);
405 static void
406 move_handle_free(struct MoveHandleChange *change)
411 Change *
412 undo_move_handle(Diagram *dia,
413 Handle *handle, Object *obj,
414 Point orig_pos, Point dest_pos)
416 struct MoveHandleChange *change;
418 change = g_new(struct MoveHandleChange, 1);
420 change->change.apply = (UndoApplyFunc) move_handle_apply;
421 change->change.revert = (UndoRevertFunc) move_handle_revert;
422 change->change.free = (UndoFreeFunc) move_handle_free;
424 change->orig_pos = orig_pos;
425 change->dest_pos = dest_pos;
426 change->handle = handle;
427 change->obj = obj;
429 DEBUG_PRINTF(("UNDO: Push new move handle at %d\n", depth(dia->undo)));
431 undo_push_change(dia->undo, (Change *) change);
432 return (Change *)change;
435 /***************** Connect object: */
437 struct ConnectChange {
438 Change change;
440 Object *obj;
441 Handle *handle;
442 ConnectionPoint *connectionpoint;
443 Point handle_pos;
446 static void
447 connect_apply(struct ConnectChange *change, Diagram *dia)
449 object_connect(change->obj, change->handle, change->connectionpoint);
451 object_add_updates(change->obj, dia);
452 change->obj->ops->move_handle(change->obj, change->handle ,
453 &change->connectionpoint->pos,
454 change->connectionpoint,
455 HANDLE_MOVE_CONNECTED, 0);
457 object_add_updates(change->obj, dia);
460 static void
461 connect_revert(struct ConnectChange *change, Diagram *dia)
463 object_unconnect(change->obj, change->handle);
465 object_add_updates(change->obj, dia);
466 change->obj->ops->move_handle(change->obj, change->handle ,
467 &change->handle_pos, NULL,
468 HANDLE_MOVE_CONNECTED, 0);
470 object_add_updates(change->obj, dia);
473 static void
474 connect_free(struct ConnectChange *change)
478 extern Change *
479 undo_connect(Diagram *dia, Object *obj, Handle *handle,
480 ConnectionPoint *connectionpoint)
482 struct ConnectChange *change;
484 change = g_new(struct ConnectChange, 1);
486 change->change.apply = (UndoApplyFunc) connect_apply;
487 change->change.revert = (UndoRevertFunc) connect_revert;
488 change->change.free = (UndoFreeFunc) connect_free;
490 change->obj = obj;
491 change->handle = handle;
492 change->handle_pos = handle->pos;
493 change->connectionpoint = connectionpoint;
495 DEBUG_PRINTF(("UNDO: Push new connect at %d\n", depth(dia->undo)));
496 undo_push_change(dia->undo, (Change *) change);
497 return (Change *)change;
500 /*************** Unconnect object: */
502 struct UnconnectChange {
503 Change change;
505 Object *obj;
506 Handle *handle;
507 ConnectionPoint *connectionpoint;
510 static void
511 unconnect_apply(struct UnconnectChange *change, Diagram *dia)
513 object_unconnect(change->obj, change->handle);
515 object_add_updates(change->obj, dia);
518 static void
519 unconnect_revert(struct UnconnectChange *change, Diagram *dia)
521 object_connect(change->obj, change->handle, change->connectionpoint);
523 object_add_updates(change->obj, dia);
526 static void
527 unconnect_free(struct UnconnectChange *change)
531 extern Change *
532 undo_unconnect(Diagram *dia, Object *obj, Handle *handle)
534 struct UnconnectChange *change;
536 change = g_new(struct UnconnectChange, 1);
538 change->change.apply = (UndoApplyFunc) unconnect_apply;
539 change->change.revert = (UndoRevertFunc) unconnect_revert;
540 change->change.free = (UndoFreeFunc) unconnect_free;
542 change->obj = obj;
543 change->handle = handle;
544 change->connectionpoint = handle->connected_to;
546 DEBUG_PRINTF(("UNDO: Push new unconnect at %d\n", depth(dia->undo)));
547 undo_push_change(dia->undo, (Change *) change);
548 return (Change *)change;
552 /******** Delete object list: */
554 struct DeleteObjectsChange {
555 Change change;
557 Layer *layer;
558 GList *obj_list; /* Owning reference when applied */
559 GList *original_objects;
560 int applied;
563 static void
564 delete_objects_apply(struct DeleteObjectsChange *change, Diagram *dia)
566 GList *list;
568 DEBUG_PRINTF(("delete_objects_apply()\n"));
569 change->applied = 1;
570 diagram_unselect_objects(dia, change->obj_list);
571 layer_remove_objects(change->layer, change->obj_list);
572 object_add_updates_list(change->obj_list, dia);
574 list = change->obj_list;
575 while (list != NULL) {
576 Object *obj = (Object *)list->data;
578 /* Have to hide any open properties dialog
579 if it contains some object in cut_list */
580 properties_hide_if_shown(dia, obj);
582 /* Remove focus if active */
583 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
584 remove_focus();
587 if (obj->parent) /* Lose references to deleted object */
588 obj->parent->children = g_list_remove(obj->parent->children, obj);
590 list = g_list_next(list);
593 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
596 static void
597 delete_objects_revert(struct DeleteObjectsChange *change, Diagram *dia)
599 GList *list;
600 DEBUG_PRINTF(("delete_objects_revert()\n"));
601 change->applied = 0;
602 layer_set_object_list(change->layer,
603 g_list_copy(change->original_objects));
604 object_add_updates_list(change->obj_list, dia);
606 list = change->obj_list;
607 while (list)
609 Object *obj = (Object *) list->data;
610 if (obj->parent) /* Restore child references */
611 obj->parent->children = g_list_append(obj->parent->children, obj);
613 list = g_list_next(list);
616 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
619 static void
620 delete_objects_free(struct DeleteObjectsChange *change)
622 DEBUG_PRINTF(("delete_objects_free()\n"));
623 if (change->applied)
624 destroy_object_list(change->obj_list);
625 else
626 g_list_free(change->obj_list);
627 g_list_free(change->original_objects);
631 This function deletes specified objects along with any children
632 they might have.
633 undo_delete_objects() only deletes the objects that are specified.
635 Change *
636 undo_delete_objects_children(Diagram *dia, GList *obj_list)
638 return undo_delete_objects(dia, parent_list_affected(obj_list));
641 Change *
642 undo_delete_objects(Diagram *dia, GList *obj_list)
644 struct DeleteObjectsChange *change;
646 change = g_new(struct DeleteObjectsChange, 1);
648 change->change.apply = (UndoApplyFunc) delete_objects_apply;
649 change->change.revert = (UndoRevertFunc) delete_objects_revert;
650 change->change.free = (UndoFreeFunc) delete_objects_free;
652 change->layer = dia->data->active_layer;
653 change->obj_list = obj_list;
654 change->original_objects = g_list_copy(dia->data->active_layer->objects);
655 change->applied = 0;
657 DEBUG_PRINTF(("UNDO: Push new delete objects at %d\n", depth(dia->undo)));
658 undo_push_change(dia->undo, (Change *) change);
659 return (Change *)change;
662 /******** Insert object list: */
664 struct InsertObjectsChange {
665 Change change;
667 Layer *layer;
668 GList *obj_list; /* Owning reference when not applied */
669 int applied;
672 static void
673 insert_objects_apply(struct InsertObjectsChange *change, Diagram *dia)
675 DEBUG_PRINTF(("insert_objects_apply()\n"));
676 change->applied = 1;
677 layer_add_objects(change->layer, g_list_copy(change->obj_list));
678 object_add_updates_list(change->obj_list, dia);
679 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
682 static void
683 insert_objects_revert(struct InsertObjectsChange *change, Diagram *dia)
685 GList *list;
687 DEBUG_PRINTF(("insert_objects_revert()\n"));
688 change->applied = 0;
689 diagram_unselect_objects(dia, change->obj_list);
690 layer_remove_objects(change->layer, change->obj_list);
691 object_add_updates_list(change->obj_list, dia);
693 list = change->obj_list;
694 while (list != NULL) {
695 Object *obj = (Object *)list->data;
697 /* Have to hide any open properties dialog
698 if it contains some object in cut_list */
699 properties_hide_if_shown(dia, obj);
701 /* Remove focus if active */
702 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
703 remove_focus();
706 list = g_list_next(list);
709 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
712 static void
713 insert_objects_free(struct InsertObjectsChange *change)
715 DEBUG_PRINTF(("insert_objects_free()\n"));
716 if (!change->applied)
717 destroy_object_list(change->obj_list);
718 else
719 g_list_free(change->obj_list);
722 Change *
723 undo_insert_objects(Diagram *dia, GList *obj_list, int applied)
725 struct InsertObjectsChange *change;
727 change = g_new(struct InsertObjectsChange, 1);
729 change->change.apply = (UndoApplyFunc) insert_objects_apply;
730 change->change.revert = (UndoRevertFunc) insert_objects_revert;
731 change->change.free = (UndoFreeFunc) insert_objects_free;
733 change->layer = dia->data->active_layer;
734 change->obj_list = obj_list;
735 change->applied = applied;
737 DEBUG_PRINTF(("UNDO: Push new insert objects at %d\n", depth(dia->undo)));
738 undo_push_change(dia->undo, (Change *) change);
739 return (Change *)change;
742 /******** Reorder object list: */
744 struct ReorderObjectsChange {
745 Change change;
747 Layer *layer;
748 GList *changed_list; /* Owning reference when applied */
749 GList *original_objects;
750 GList *reordered_objects;
753 static void
754 reorder_objects_apply(struct ReorderObjectsChange *change, Diagram *dia)
756 DEBUG_PRINTF(("reorder_objects_apply()\n"));
757 layer_set_object_list(change->layer,
758 g_list_copy(change->reordered_objects));
759 object_add_updates_list(change->changed_list, dia);
762 static void
763 reorder_objects_revert(struct ReorderObjectsChange *change, Diagram *dia)
765 DEBUG_PRINTF(("reorder_objects_revert()\n"));
766 layer_set_object_list(change->layer,
767 g_list_copy(change->original_objects));
768 object_add_updates_list(change->changed_list, dia);
771 static void
772 reorder_objects_free(struct ReorderObjectsChange *change)
774 DEBUG_PRINTF(("reorder_objects_free()\n"));
775 g_list_free(change->changed_list);
776 g_list_free(change->original_objects);
777 g_list_free(change->reordered_objects);
780 Change *
781 undo_reorder_objects(Diagram *dia, GList *changed_list, GList *orig_list)
783 struct ReorderObjectsChange *change;
785 change = g_new(struct ReorderObjectsChange, 1);
787 change->change.apply = (UndoApplyFunc) reorder_objects_apply;
788 change->change.revert = (UndoRevertFunc) reorder_objects_revert;
789 change->change.free = (UndoFreeFunc) reorder_objects_free;
791 change->layer = dia->data->active_layer;
792 change->changed_list = changed_list;
793 change->original_objects = orig_list;
794 change->reordered_objects = g_list_copy(dia->data->active_layer->objects);
796 DEBUG_PRINTF(("UNDO: Push new reorder objects at %d\n", depth(dia->undo)));
797 undo_push_change(dia->undo, (Change *) change);
798 return (Change *)change;
801 /******** ObjectChange: */
803 struct ObjectChangeChange {
804 Change change;
806 Object *obj;
807 ObjectChange *obj_change;
811 static void
812 object_change_apply(struct ObjectChangeChange *change,
813 Diagram *dia)
815 object_add_updates(change->obj, dia);
816 change->obj_change->apply(change->obj_change, change->obj);
817 { /* Make sure object updates its data: */
818 Point p = change->obj->position;
819 (change->obj->ops->move)(change->obj,&p);
821 object_add_updates(change->obj, dia);
823 diagram_update_connections_object(dia, change->obj, TRUE);
825 properties_update_if_shown(dia, change->obj);
828 static void
829 object_change_revert(struct ObjectChangeChange *change,
830 Diagram *dia)
832 object_add_updates(change->obj, dia);
833 change->obj_change->revert(change->obj_change, change->obj);
834 { /* Make sure object updates its data: */
835 Point p = change->obj->position;
836 (change->obj->ops->move)(change->obj,&p);
838 object_add_updates(change->obj, dia);
840 diagram_update_connections_object(dia, change->obj, TRUE);
842 properties_update_if_shown(dia, change->obj);
845 static void
846 object_change_free(struct ObjectChangeChange *change)
848 DEBUG_PRINTF(("state_change_free()\n"));
849 if (change->obj_change->free)
850 (*change->obj_change->free)(change->obj_change);
851 g_free(change->obj_change);
854 Change *
855 undo_object_change(Diagram *dia, Object *obj,
856 ObjectChange *obj_change)
858 struct ObjectChangeChange *change;
860 change = g_new(struct ObjectChangeChange, 1);
862 change->change.apply = (UndoApplyFunc) object_change_apply;
863 change->change.revert = (UndoRevertFunc) object_change_revert;
864 change->change.free = (UndoFreeFunc) object_change_free;
866 change->obj = obj;
867 change->obj_change = obj_change;
869 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
870 undo_push_change(dia->undo, (Change *) change);
871 return (Change *)change;
874 /******** Group object list: */
876 /** Grouping and ungrouping are two subtly different changes: While
877 * ungrouping preserves the front/back position, grouping cannot do that,
878 * since the act of grouping destroys positions for the members of the
879 * group, and those positions have to be restored in the undo.
882 struct GroupObjectsChange {
883 Change change;
885 Layer *layer;
886 Object *group; /* owning reference if not applied */
887 GList *obj_list; /* This list is owned by the group. */
888 GList *orig_list; /* This list (not the object) is owned */
889 int applied;
892 static void
893 group_objects_apply(struct GroupObjectsChange *change, Diagram *dia)
895 GList *list;
897 DEBUG_PRINTF(("group_objects_apply()\n"));
899 change->applied = 1;
901 diagram_unselect_objects(dia, change->obj_list);
902 layer_remove_objects(change->layer, change->obj_list);
903 layer_add_object(change->layer, change->group);
904 object_add_updates(change->group, dia);
906 list = change->obj_list;
907 while (list != NULL) {
908 Object *obj = (Object *)list->data;
910 /* Have to hide any open properties dialog
911 if it contains some object in cut_list */
912 properties_hide_if_shown(dia, obj);
914 /* Remove focus if active */
915 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
916 remove_focus();
919 list = g_list_next(list);
922 diagram_tree_add_object(diagram_tree(), dia, change->group);
923 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
926 static void
927 group_objects_revert(struct GroupObjectsChange *change, Diagram *dia)
929 GList *old_list;
931 DEBUG_PRINTF(("group_objects_revert()\n"));
932 change->applied = 0;
934 diagram_unselect_object(dia, change->group);
935 object_add_updates(change->group, dia);
937 old_list = change->layer->objects;
938 layer_set_object_list(change->layer, g_list_copy(change->orig_list));
939 g_list_free(old_list);
941 object_add_updates_list(change->obj_list, dia);
943 properties_hide_if_shown(dia, change->group);
945 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
946 diagram_tree_remove_object(diagram_tree(), change->group);
949 static void
950 group_objects_free(struct GroupObjectsChange *change)
952 DEBUG_PRINTF(("group_objects_free()\n"));
953 if (!change->applied) {
954 group_destroy_shallow(change->group);
955 change->group = NULL;
956 change->obj_list = NULL;
958 g_list_free(change->orig_list);
961 Change *
962 undo_group_objects(Diagram *dia, GList *obj_list, Object *group,
963 GList *orig_list)
965 struct GroupObjectsChange *change;
967 change = g_new(struct GroupObjectsChange, 1);
969 change->change.apply = (UndoApplyFunc) group_objects_apply;
970 change->change.revert = (UndoRevertFunc) group_objects_revert;
971 change->change.free = (UndoFreeFunc) group_objects_free;
973 change->layer = dia->data->active_layer;
974 change->group = group;
975 change->obj_list = obj_list;
976 change->orig_list = orig_list;
977 change->applied = 1;
979 DEBUG_PRINTF(("UNDO: Push new group objects at %d\n", depth(dia->undo)));
980 undo_push_change(dia->undo, (Change *) change);
981 return (Change *)change;
984 /******** Ungroup object list: */
986 struct UngroupObjectsChange {
987 Change change;
989 Layer *layer;
990 Object *group; /* owning reference if applied */
991 GList *obj_list; /* This list is owned by the ungroup. */
992 int group_index;
993 int applied;
996 static void
997 ungroup_objects_apply(struct UngroupObjectsChange *change, Diagram *dia)
999 DEBUG_PRINTF(("ungroup_objects_apply()\n"));
1001 change->applied = 1;
1003 diagram_unselect_object(dia, change->group);
1004 object_add_updates(change->group, dia);
1005 layer_replace_object_with_list(change->layer, change->group,
1006 g_list_copy(change->obj_list));
1007 object_add_updates_list(change->obj_list, dia);
1009 properties_hide_if_shown(dia, change->group);
1011 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
1012 diagram_tree_remove_object(diagram_tree(), change->group);
1015 static void
1016 ungroup_objects_revert(struct UngroupObjectsChange *change, Diagram *dia)
1018 GList *list;
1020 DEBUG_PRINTF(("ungroup_objects_revert()\n"));
1021 change->applied = 0;
1024 diagram_unselect_objects(dia, change->obj_list);
1025 layer_remove_objects(change->layer, change->obj_list);
1026 layer_add_object_at(change->layer, change->group, change->group_index);
1027 object_add_updates(change->group, dia);
1029 list = change->obj_list;
1030 while (list != NULL) {
1031 Object *obj = (Object *)list->data;
1033 /* Have to hide any open properties dialog
1034 if it contains some object in cut_list */
1035 properties_hide_if_shown(dia, obj);
1037 /* Remove focus if active */
1038 if ((active_focus()!=NULL) && (active_focus()->obj == obj)) {
1039 remove_focus();
1042 list = g_list_next(list);
1045 diagram_tree_add_object(diagram_tree(), dia, change->group);
1046 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
1049 static void
1050 ungroup_objects_free(struct UngroupObjectsChange *change)
1052 DEBUG_PRINTF(("ungroup_objects_free()\n"));
1053 if (change->applied) {
1054 group_destroy_shallow(change->group);
1055 change->group = NULL;
1056 change->obj_list = NULL;
1060 Change *
1061 undo_ungroup_objects(Diagram *dia, GList *obj_list, Object *group,
1062 int group_index)
1064 struct UngroupObjectsChange *change;
1066 change = g_new(struct UngroupObjectsChange, 1);
1068 change->change.apply = (UndoApplyFunc) ungroup_objects_apply;
1069 change->change.revert = (UndoRevertFunc) ungroup_objects_revert;
1070 change->change.free = (UndoFreeFunc) ungroup_objects_free;
1072 change->layer = dia->data->active_layer;
1073 change->group = group;
1074 change->obj_list = obj_list;
1075 change->group_index = group_index;
1076 change->applied = 1;
1078 DEBUG_PRINTF(("UNDO: Push new ungroup objects at %d\n", depth(dia->undo)));
1079 undo_push_change(dia->undo, (Change *) change);
1080 return (Change *)change;
1083 /******* PARENTING */
1085 struct ParentChange {
1086 Change change;
1087 Object *parentobj, *childobj;
1088 gboolean parent;
1091 /** Performs the actual parenting of a child to a parent.
1092 * Since no display changes arise from this, we need call no update. */
1093 static void
1094 parent_object(Diagram *dia, Object *parent, Object *child)
1096 child->parent = parent;
1097 parent->children = g_list_prepend(parent->children, child);
1100 /** Performs the actual removal of a child from a parent.
1101 * Since no display changes arise from this, we need call no update. */
1102 static void
1103 unparent_object(Diagram *dia, Object *parent, Object *child)
1105 child->parent = NULL;
1106 parent->children = g_list_remove(parent->children, child);
1109 /** Applies the given ParentChange */
1110 static void
1111 parent_change_apply(Change *change, Diagram *dia)
1113 struct ParentChange *parentchange = (struct ParentChange*)change;
1114 if (parentchange->parent) {
1115 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1116 } else {
1117 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1121 /** Reverts the given ParentChange */
1122 static void
1123 parent_change_revert(Change *change, Diagram *dia)
1125 struct ParentChange *parentchange = (struct ParentChange*)change;
1126 if (!parentchange->parent) {
1127 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1128 } else {
1129 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1133 /** Frees items in the change -- none really */
1134 static void
1135 parent_change_free(Change *change)
1139 /** Create a new Change object for parenting/unparenting.
1140 * `parent' is TRUE if applying this change makes childobj a
1141 * child of parentobj.
1143 Change *
1144 undo_parenting(Diagram *dia, Object* parentobj, Object* childobj,
1145 gboolean parent)
1147 struct ParentChange *parentchange = g_new0(struct ParentChange, 1);
1148 Change *change = (Change*)parentchange;
1149 change->apply = parent_change_apply;
1150 change->revert = parent_change_revert;
1151 change->free = parent_change_free;
1153 parentchange->parentobj = parentobj;
1154 parentchange->childobj = childobj;
1155 parentchange->parent = parent;
1157 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
1158 undo_push_change(dia->undo, change);
1159 return change;