New cisco icons, and a fix in element.h
[dia.git] / app / undo.c
blob25f6987dec9ca2c53d112b490f27f6c6769de6fa
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"
29 #include "textedit.h"
31 #if 0
32 #define DEBUG_PRINTF(args) printf args
33 #else
34 #define DEBUG_PRINTF(args)
35 #endif
37 static void
38 transaction_point_pointer(Change *change, Diagram *dia)
40 /* Empty function used to track transactionpoints. */
43 static int
44 is_transactionpoint(Change *change)
46 return change->apply == transaction_point_pointer;
49 static Change *
50 new_transactionpoint(void)
52 Change *transaction = g_new0(Change, 1);
54 if (transaction) {
55 transaction->apply = transaction_point_pointer;
56 transaction->revert = transaction_point_pointer;
57 transaction->free = NULL;
60 return transaction;
63 UndoStack *
64 new_undo_stack(Diagram *dia)
66 UndoStack *stack;
67 Change *transaction;
69 stack = g_new(UndoStack, 1);
70 if (stack!=NULL){
71 stack->dia = dia;
72 transaction = new_transactionpoint();
73 transaction->next = transaction->prev = NULL;
74 stack->last_change = transaction;
75 stack->current_change = transaction;
76 stack->last_save = transaction;
77 stack->depth = 0;
79 return stack;
82 void
83 undo_destroy(UndoStack *stack)
85 undo_clear(stack);
86 g_free(stack->current_change); /* Free first transaction point. */
87 g_free(stack);
92 static void
93 undo_remove_redo_info(UndoStack *stack)
95 Change *change;
96 Change *next_change;
98 DEBUG_PRINTF(("UNDO: Removing redo info\n"));
100 change = stack->current_change->next;
101 stack->current_change->next = NULL;
102 stack->last_change = stack->current_change;
104 while (change != NULL) {
105 next_change = change->next;
106 if (change->free)
107 (change->free)(change);
108 g_free(change);
109 change = next_change;
113 void
114 undo_push_change(UndoStack *stack, Change *change)
116 if (stack->current_change != stack->last_change)
117 undo_remove_redo_info(stack);
119 DEBUG_PRINTF(("UNDO: Push new change at %d\n", depth(stack)));
121 change->prev = stack->last_change;
122 change->next = NULL;
123 stack->last_change->next = change;
124 stack->last_change = change;
125 stack->current_change = change;
128 static void
129 undo_delete_lowest_transaction(UndoStack *stack)
131 Change *change;
132 Change *next_change;
134 /* Find the lowest change: */
135 change = stack->current_change;
136 while (change->prev != NULL) {
137 change = change->prev;
140 /* Remove changes from the bottom until (and including)
141 * the first transactionpoint.
142 * Stop if we reach current_change or NULL.
144 do {
145 if ( (change == NULL) || (change == stack->current_change))
146 break;
148 next_change = change->next;
149 DEBUG_PRINTF(("freeing one change from the bottom.\n"));
150 if (change->free)
151 (change->free)(change);
152 g_free(change);
153 change = next_change;
154 } while (!is_transactionpoint(change));
156 if (is_transactionpoint(change)) {
157 stack->depth--;
158 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
160 change->prev = NULL;
163 void
164 undo_set_transactionpoint(UndoStack *stack)
166 Change *transaction;
168 if (is_transactionpoint(stack->current_change))
169 return;
171 DEBUG_PRINTF(("UNDO: Push new transactionpoint at %d\n", depth(stack)));
173 transaction = new_transactionpoint();
175 undo_push_change(stack, transaction);
176 stack->depth++;
177 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
179 if (prefs.undo_depth > 0) {
180 while (stack->depth > prefs.undo_depth){
181 undo_delete_lowest_transaction(stack);
186 void
187 undo_revert_to_last_tp(UndoStack *stack)
189 Change *change;
190 Change *prev_change;
192 if (stack->current_change->prev == NULL)
193 return; /* Can't revert first transactionpoint */
195 change = stack->current_change;
196 do {
197 prev_change = change->prev;
198 (change->revert)(change, stack->dia);
199 change = prev_change;
200 } while (!is_transactionpoint(change));
201 stack->current_change = change;
202 stack->depth--;
203 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
206 void
207 undo_apply_to_next_tp(UndoStack *stack)
209 Change *change;
210 Change *next_change;
212 change = stack->current_change;
214 if (change->next == NULL)
215 return /* Already at top. */;
217 do {
218 next_change = change->next;
219 (change->apply)(change, stack->dia);
220 change = next_change;
221 } while ( (change != NULL) &&
222 (!is_transactionpoint(change)) );
223 if (change == NULL)
224 change = stack->last_change;
225 stack->current_change = change;
226 stack->depth++;
227 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
231 void
232 undo_clear(UndoStack *stack)
234 Change *change;
236 DEBUG_PRINTF(("undo_clear()\n"));
238 change = stack->current_change;
240 while (change->prev != NULL) {
241 change = change->prev;
244 stack->current_change = change;
245 stack->depth = 0;
246 undo_remove_redo_info(stack);
249 /** Marks the undo stack at the time of a save.
251 void
252 undo_mark_save(UndoStack *stack)
254 stack->last_save = stack->current_change;
257 /** Returns true if the diagram is undo-wise in the same state as at
258 * last save, i.e. the current change is the same as it was at last save.
260 gboolean
261 undo_is_saved(UndoStack *stack)
263 return stack->last_save == stack->current_change;
268 /****************************************************************/
269 /****************************************************************/
270 /***************** *********************/
271 /***************** Specific undo functions: *********************/
272 /***************** *********************/
273 /****************************************************************/
274 /****************************************************************/
276 /******** Move object list: */
278 struct MoveObjectsChange {
279 Change change;
281 Point *orig_pos;
282 Point *dest_pos;
283 GList *obj_list;
286 static void
287 move_objects_apply(struct MoveObjectsChange *change, Diagram *dia)
289 GList *list;
290 int i;
291 DiaObject *obj;
293 object_add_updates_list(change->obj_list, dia);
295 list = change->obj_list;
296 i=0;
297 while (list != NULL) {
298 obj = (DiaObject *) list->data;
300 obj->ops->move(obj, &change->dest_pos[i]);
302 list = g_list_next(list); i++;
305 list = change->obj_list;
306 while (list!=NULL) {
307 obj = (DiaObject *) list->data;
309 diagram_update_connections_object(dia, obj, TRUE);
311 list = g_list_next(list);
314 object_add_updates_list(change->obj_list, dia);
317 static void
318 move_objects_revert(struct MoveObjectsChange *change, Diagram *dia)
320 GList *list;
321 int i;
322 DiaObject *obj;
324 object_add_updates_list(change->obj_list, dia);
326 list = change->obj_list;
327 i=0;
328 while (list != NULL) {
329 obj = (DiaObject *) list->data;
331 obj->ops->move(obj, &change->orig_pos[i]);
333 list = g_list_next(list); i++;
336 list = change->obj_list;
337 while (list!=NULL) {
338 obj = (DiaObject *) list->data;
340 diagram_update_connections_object(dia, obj, TRUE);
342 list = g_list_next(list);
345 object_add_updates_list(change->obj_list, dia);
348 static void
349 move_objects_free(struct MoveObjectsChange *change)
351 g_free(change->orig_pos);
352 g_free(change->dest_pos);
353 g_list_free(change->obj_list);
356 extern Change *
357 undo_move_objects(Diagram *dia, Point *orig_pos, Point *dest_pos,
358 GList *obj_list)
360 struct MoveObjectsChange *change;
362 change = g_new0(struct MoveObjectsChange, 1);
364 change->change.apply = (UndoApplyFunc) move_objects_apply;
365 change->change.revert = (UndoRevertFunc) move_objects_revert;
366 change->change.free = (UndoFreeFunc) move_objects_free;
368 change->orig_pos = orig_pos;
369 change->dest_pos = dest_pos;
370 change->obj_list = obj_list;
372 DEBUG_PRINTF(("UNDO: Push new move objects at %d\n", depth(dia->undo)));
373 undo_push_change(dia->undo, (Change *) change);
374 return (Change *)change;
377 /********** Move handle: */
379 struct MoveHandleChange {
380 Change change;
382 Point orig_pos;
383 Point dest_pos;
384 Handle *handle;
385 DiaObject *obj;
388 static void
389 move_handle_apply(struct MoveHandleChange *change, Diagram *dia)
391 object_add_updates(change->obj, dia);
392 change->obj->ops->move_handle(change->obj, change->handle,
393 &change->dest_pos, NULL,
394 HANDLE_MOVE_USER_FINAL, 0);
395 object_add_updates(change->obj, dia);
396 diagram_update_connections_object(dia, change->obj, TRUE);
399 static void
400 move_handle_revert(struct MoveHandleChange *change, Diagram *dia)
402 object_add_updates(change->obj, dia);
403 change->obj->ops->move_handle(change->obj, change->handle,
404 &change->orig_pos, NULL,
405 HANDLE_MOVE_USER_FINAL, 0);
406 object_add_updates(change->obj, dia);
407 diagram_update_connections_object(dia, change->obj, TRUE);
410 static void
411 move_handle_free(struct MoveHandleChange *change)
416 Change *
417 undo_move_handle(Diagram *dia,
418 Handle *handle, DiaObject *obj,
419 Point orig_pos, Point dest_pos)
421 struct MoveHandleChange *change;
423 change = g_new0(struct MoveHandleChange, 1);
425 change->change.apply = (UndoApplyFunc) move_handle_apply;
426 change->change.revert = (UndoRevertFunc) move_handle_revert;
427 change->change.free = (UndoFreeFunc) move_handle_free;
429 change->orig_pos = orig_pos;
430 change->dest_pos = dest_pos;
431 change->handle = handle;
432 change->obj = obj;
434 DEBUG_PRINTF(("UNDO: Push new move handle at %d\n", depth(dia->undo)));
436 undo_push_change(dia->undo, (Change *) change);
437 return (Change *)change;
440 /***************** Connect object: */
442 struct ConnectChange {
443 Change change;
445 DiaObject *obj;
446 Handle *handle;
447 ConnectionPoint *connectionpoint;
448 Point handle_pos;
451 static void
452 connect_apply(struct ConnectChange *change, Diagram *dia)
454 object_connect(change->obj, change->handle, change->connectionpoint);
456 object_add_updates(change->obj, dia);
457 change->obj->ops->move_handle(change->obj, change->handle ,
458 &change->connectionpoint->pos,
459 change->connectionpoint,
460 HANDLE_MOVE_CONNECTED, 0);
462 object_add_updates(change->obj, dia);
465 static void
466 connect_revert(struct ConnectChange *change, Diagram *dia)
468 object_unconnect(change->obj, change->handle);
470 object_add_updates(change->obj, dia);
471 change->obj->ops->move_handle(change->obj, change->handle ,
472 &change->handle_pos, NULL,
473 HANDLE_MOVE_CONNECTED, 0);
475 object_add_updates(change->obj, dia);
478 static void
479 connect_free(struct ConnectChange *change)
483 extern Change *
484 undo_connect(Diagram *dia, DiaObject *obj, Handle *handle,
485 ConnectionPoint *connectionpoint)
487 struct ConnectChange *change;
489 change = g_new0(struct ConnectChange, 1);
491 change->change.apply = (UndoApplyFunc) connect_apply;
492 change->change.revert = (UndoRevertFunc) connect_revert;
493 change->change.free = (UndoFreeFunc) connect_free;
495 change->obj = obj;
496 change->handle = handle;
497 change->handle_pos = handle->pos;
498 change->connectionpoint = connectionpoint;
500 DEBUG_PRINTF(("UNDO: Push new connect at %d\n", depth(dia->undo)));
501 undo_push_change(dia->undo, (Change *) change);
502 return (Change *)change;
505 /*************** Unconnect object: */
507 struct UnconnectChange {
508 Change change;
510 DiaObject *obj;
511 Handle *handle;
512 ConnectionPoint *connectionpoint;
515 static void
516 unconnect_apply(struct UnconnectChange *change, Diagram *dia)
518 object_unconnect(change->obj, change->handle);
520 object_add_updates(change->obj, dia);
523 static void
524 unconnect_revert(struct UnconnectChange *change, Diagram *dia)
526 object_connect(change->obj, change->handle, change->connectionpoint);
528 object_add_updates(change->obj, dia);
531 static void
532 unconnect_free(struct UnconnectChange *change)
536 extern Change *
537 undo_unconnect(Diagram *dia, DiaObject *obj, Handle *handle)
539 struct UnconnectChange *change;
541 change = g_new0(struct UnconnectChange, 1);
543 change->change.apply = (UndoApplyFunc) unconnect_apply;
544 change->change.revert = (UndoRevertFunc) unconnect_revert;
545 change->change.free = (UndoFreeFunc) unconnect_free;
547 change->obj = obj;
548 change->handle = handle;
549 change->connectionpoint = handle->connected_to;
551 DEBUG_PRINTF(("UNDO: Push new unconnect at %d\n", depth(dia->undo)));
552 undo_push_change(dia->undo, (Change *) change);
553 return (Change *)change;
557 /******** Delete object list: */
559 struct DeleteObjectsChange {
560 Change change;
562 Layer *layer;
563 GList *obj_list; /* Owning reference when applied */
564 GList *original_objects;
565 int applied;
568 static void
569 delete_objects_apply(struct DeleteObjectsChange *change, Diagram *dia)
571 GList *list;
573 DEBUG_PRINTF(("delete_objects_apply()\n"));
574 change->applied = 1;
575 diagram_unselect_objects(dia, change->obj_list);
576 layer_remove_objects(change->layer, change->obj_list);
577 object_add_updates_list(change->obj_list, dia);
579 list = change->obj_list;
580 while (list != NULL) {
581 DiaObject *obj = (DiaObject *)list->data;
583 /* Have to hide any open properties dialog
584 if it contains some object in cut_list */
585 properties_hide_if_shown(dia, obj);
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 DiaObject *obj = (DiaObject *) 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_new0(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 DiaObject *obj = (DiaObject *)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 list = g_list_next(list);
704 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
707 static void
708 insert_objects_free(struct InsertObjectsChange *change)
710 DEBUG_PRINTF(("insert_objects_free()\n"));
711 if (!change->applied)
712 destroy_object_list(change->obj_list);
713 else
714 g_list_free(change->obj_list);
717 Change *
718 undo_insert_objects(Diagram *dia, GList *obj_list, int applied)
720 struct InsertObjectsChange *change;
722 change = g_new0(struct InsertObjectsChange, 1);
724 change->change.apply = (UndoApplyFunc) insert_objects_apply;
725 change->change.revert = (UndoRevertFunc) insert_objects_revert;
726 change->change.free = (UndoFreeFunc) insert_objects_free;
728 change->layer = dia->data->active_layer;
729 change->obj_list = obj_list;
730 change->applied = applied;
732 DEBUG_PRINTF(("UNDO: Push new insert objects at %d\n", depth(dia->undo)));
733 undo_push_change(dia->undo, (Change *) change);
734 return (Change *)change;
737 /******** Reorder object list: */
739 struct ReorderObjectsChange {
740 Change change;
742 Layer *layer;
743 GList *changed_list; /* Owning reference when applied */
744 GList *original_objects;
745 GList *reordered_objects;
748 static void
749 reorder_objects_apply(struct ReorderObjectsChange *change, Diagram *dia)
751 DEBUG_PRINTF(("reorder_objects_apply()\n"));
752 layer_set_object_list(change->layer,
753 g_list_copy(change->reordered_objects));
754 object_add_updates_list(change->changed_list, dia);
757 static void
758 reorder_objects_revert(struct ReorderObjectsChange *change, Diagram *dia)
760 DEBUG_PRINTF(("reorder_objects_revert()\n"));
761 layer_set_object_list(change->layer,
762 g_list_copy(change->original_objects));
763 object_add_updates_list(change->changed_list, dia);
766 static void
767 reorder_objects_free(struct ReorderObjectsChange *change)
769 DEBUG_PRINTF(("reorder_objects_free()\n"));
770 g_list_free(change->changed_list);
771 g_list_free(change->original_objects);
772 g_list_free(change->reordered_objects);
775 Change *
776 undo_reorder_objects(Diagram *dia, GList *changed_list, GList *orig_list)
778 struct ReorderObjectsChange *change;
780 change = g_new0(struct ReorderObjectsChange, 1);
782 change->change.apply = (UndoApplyFunc) reorder_objects_apply;
783 change->change.revert = (UndoRevertFunc) reorder_objects_revert;
784 change->change.free = (UndoFreeFunc) reorder_objects_free;
786 change->layer = dia->data->active_layer;
787 change->changed_list = changed_list;
788 change->original_objects = orig_list;
789 change->reordered_objects = g_list_copy(dia->data->active_layer->objects);
791 DEBUG_PRINTF(("UNDO: Push new reorder objects at %d\n", depth(dia->undo)));
792 undo_push_change(dia->undo, (Change *) change);
793 return (Change *)change;
796 /******** ObjectChange: */
798 struct ObjectChangeChange {
799 Change change;
801 DiaObject *obj;
802 ObjectChange *obj_change;
806 static void
807 object_change_apply(struct ObjectChangeChange *change,
808 Diagram *dia)
810 object_add_updates(change->obj, dia);
811 change->obj_change->apply(change->obj_change, change->obj);
812 { /* Make sure object updates its data: */
813 Point p = change->obj->position;
814 (change->obj->ops->move)(change->obj,&p);
816 object_add_updates(change->obj, dia);
818 diagram_update_connections_object(dia, change->obj, TRUE);
820 properties_update_if_shown(dia, change->obj);
823 static void
824 object_change_revert(struct ObjectChangeChange *change,
825 Diagram *dia)
827 object_add_updates(change->obj, dia);
828 change->obj_change->revert(change->obj_change, change->obj);
829 { /* Make sure object updates its data: */
830 Point p = change->obj->position;
831 (change->obj->ops->move)(change->obj,&p);
833 object_add_updates(change->obj, dia);
835 diagram_update_connections_object(dia, change->obj, TRUE);
837 properties_update_if_shown(dia, change->obj);
840 static void
841 object_change_free(struct ObjectChangeChange *change)
843 DEBUG_PRINTF(("state_change_free()\n"));
844 if (change->obj_change->free)
845 (*change->obj_change->free)(change->obj_change);
846 g_free(change->obj_change);
849 Change *
850 undo_object_change(Diagram *dia, DiaObject *obj,
851 ObjectChange *obj_change)
853 struct ObjectChangeChange *change;
855 change = g_new0(struct ObjectChangeChange, 1);
857 change->change.apply = (UndoApplyFunc) object_change_apply;
858 change->change.revert = (UndoRevertFunc) object_change_revert;
859 change->change.free = (UndoFreeFunc) object_change_free;
861 change->obj = obj;
862 change->obj_change = obj_change;
864 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
865 undo_push_change(dia->undo, (Change *) change);
866 return (Change *)change;
869 /******** Group object list: */
871 /** Grouping and ungrouping are two subtly different changes: While
872 * ungrouping preserves the front/back position, grouping cannot do that,
873 * since the act of grouping destroys positions for the members of the
874 * group, and those positions have to be restored in the undo.
877 struct GroupObjectsChange {
878 Change change;
880 Layer *layer;
881 DiaObject *group; /* owning reference if not applied */
882 GList *obj_list; /* The list of objects in this group. Owned by the
883 group */
884 GList *orig_list; /* A copy of the original list of all objects,
885 from before the group was created. Owned by
886 the group */
887 int applied;
890 static void
891 group_objects_apply(struct GroupObjectsChange *change, Diagram *dia)
893 GList *list;
895 DEBUG_PRINTF(("group_objects_apply()\n"));
897 change->applied = 1;
899 diagram_unselect_objects(dia, change->obj_list);
900 layer_remove_objects(change->layer, change->obj_list);
901 layer_add_object(change->layer, change->group);
902 object_add_updates(change->group, dia);
904 list = change->obj_list;
905 while (list != NULL) {
906 DiaObject *obj = (DiaObject *)list->data;
908 /* Have to hide any open properties dialog
909 if it contains some object in cut_list */
910 properties_hide_if_shown(dia, obj);
912 object_add_updates(obj, dia);
914 list = g_list_next(list);
917 diagram_tree_add_object(diagram_tree(), dia, change->group);
918 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
921 static void
922 group_objects_revert(struct GroupObjectsChange *change, Diagram *dia)
924 DEBUG_PRINTF(("group_objects_revert()\n"));
925 change->applied = 0;
927 diagram_unselect_object(dia, change->group);
928 object_add_updates(change->group, dia);
930 layer_set_object_list(change->layer, g_list_copy(change->orig_list));
932 object_add_updates_list(change->obj_list, dia);
934 properties_hide_if_shown(dia, change->group);
936 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
937 diagram_tree_remove_object(diagram_tree(), change->group);
940 static void
941 group_objects_free(struct GroupObjectsChange *change)
943 DEBUG_PRINTF(("group_objects_free()\n"));
944 if (!change->applied) {
945 group_destroy_shallow(change->group);
946 change->group = NULL;
947 change->obj_list = NULL;
948 /** Leak here? */
950 g_list_free(change->orig_list);
953 Change *
954 undo_group_objects(Diagram *dia, GList *obj_list, DiaObject *group,
955 GList *orig_list)
957 struct GroupObjectsChange *change;
959 change = g_new0(struct GroupObjectsChange, 1);
961 change->change.apply = (UndoApplyFunc) group_objects_apply;
962 change->change.revert = (UndoRevertFunc) group_objects_revert;
963 change->change.free = (UndoFreeFunc) group_objects_free;
965 change->layer = dia->data->active_layer;
966 change->group = group;
967 change->obj_list = obj_list;
968 change->orig_list = orig_list;
969 change->applied = 1;
971 DEBUG_PRINTF(("UNDO: Push new group objects at %d\n", depth(dia->undo)));
972 undo_push_change(dia->undo, (Change *) change);
973 return (Change *)change;
976 /******** Ungroup object list: */
978 struct UngroupObjectsChange {
979 Change change;
981 Layer *layer;
982 DiaObject *group; /* owning reference if applied */
983 GList *obj_list; /* This list is owned by the ungroup. */
984 int group_index;
985 int applied;
988 static void
989 ungroup_objects_apply(struct UngroupObjectsChange *change, Diagram *dia)
991 DEBUG_PRINTF(("ungroup_objects_apply()\n"));
993 change->applied = 1;
995 diagram_unselect_object(dia, change->group);
996 object_add_updates(change->group, dia);
997 layer_replace_object_with_list(change->layer, change->group,
998 g_list_copy(change->obj_list));
999 object_add_updates_list(change->obj_list, dia);
1001 properties_hide_if_shown(dia, change->group);
1003 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
1004 diagram_tree_remove_object(diagram_tree(), change->group);
1007 static void
1008 ungroup_objects_revert(struct UngroupObjectsChange *change, Diagram *dia)
1010 GList *list;
1012 DEBUG_PRINTF(("ungroup_objects_revert()\n"));
1013 change->applied = 0;
1016 diagram_unselect_objects(dia, change->obj_list);
1017 layer_remove_objects(change->layer, change->obj_list);
1018 layer_add_object_at(change->layer, change->group, change->group_index);
1019 object_add_updates(change->group, dia);
1021 list = change->obj_list;
1022 while (list != NULL) {
1023 DiaObject *obj = (DiaObject *)list->data;
1025 /* Have to hide any open properties dialog
1026 if it contains some object in cut_list */
1027 properties_hide_if_shown(dia, obj);
1029 list = g_list_next(list);
1032 diagram_tree_add_object(diagram_tree(), dia, change->group);
1033 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
1036 static void
1037 ungroup_objects_free(struct UngroupObjectsChange *change)
1039 DEBUG_PRINTF(("ungroup_objects_free()\n"));
1040 if (change->applied) {
1041 group_destroy_shallow(change->group);
1042 change->group = NULL;
1043 change->obj_list = NULL;
1047 Change *
1048 undo_ungroup_objects(Diagram *dia, GList *obj_list, DiaObject *group,
1049 int group_index)
1051 struct UngroupObjectsChange *change;
1053 change = g_new0(struct UngroupObjectsChange, 1);
1055 change->change.apply = (UndoApplyFunc) ungroup_objects_apply;
1056 change->change.revert = (UndoRevertFunc) ungroup_objects_revert;
1057 change->change.free = (UndoFreeFunc) ungroup_objects_free;
1059 change->layer = dia->data->active_layer;
1060 change->group = group;
1061 change->obj_list = obj_list;
1062 change->group_index = group_index;
1063 change->applied = 1;
1065 DEBUG_PRINTF(("UNDO: Push new ungroup objects at %d\n", depth(dia->undo)));
1066 undo_push_change(dia->undo, (Change *) change);
1067 return (Change *)change;
1070 /******* PARENTING */
1072 struct ParentChange {
1073 Change change;
1074 DiaObject *parentobj, *childobj;
1075 gboolean parent;
1078 /** Performs the actual parenting of a child to a parent.
1079 * Since no display changes arise from this, we need call no update. */
1080 static void
1081 parent_object(Diagram *dia, DiaObject *parent, DiaObject *child)
1083 child->parent = parent;
1084 parent->children = g_list_prepend(parent->children, child);
1087 /** Performs the actual removal of a child from a parent.
1088 * Since no display changes arise from this, we need call no update. */
1089 static void
1090 unparent_object(Diagram *dia, DiaObject *parent, DiaObject *child)
1092 child->parent = NULL;
1093 parent->children = g_list_remove(parent->children, child);
1096 /** Applies the given ParentChange */
1097 static void
1098 parent_change_apply(Change *change, Diagram *dia)
1100 struct ParentChange *parentchange = (struct ParentChange*)change;
1101 if (parentchange->parent) {
1102 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1103 } else {
1104 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1108 /** Reverts the given ParentChange */
1109 static void
1110 parent_change_revert(Change *change, Diagram *dia)
1112 struct ParentChange *parentchange = (struct ParentChange*)change;
1113 if (!parentchange->parent) {
1114 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1115 } else {
1116 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1120 /** Frees items in the change -- none really */
1121 static void
1122 parent_change_free(Change *change)
1126 /** Create a new Change object for parenting/unparenting.
1127 * `parent' is TRUE if applying this change makes childobj a
1128 * child of parentobj.
1130 Change *
1131 undo_parenting(Diagram *dia, DiaObject* parentobj, DiaObject* childobj,
1132 gboolean parent)
1134 struct ParentChange *parentchange = g_new0(struct ParentChange, 1);
1135 Change *change = (Change*)parentchange;
1136 change->apply = parent_change_apply;
1137 change->revert = parent_change_revert;
1138 change->free = parent_change_free;
1140 parentchange->parentobj = parentobj;
1141 parentchange->childobj = childobj;
1142 parentchange->parent = parent;
1144 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
1145 undo_push_change(dia->undo, change);
1146 return change;