1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
3 *
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.
8 *
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.
13 *
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.
17 */
18
19 #include <config.h>
20
21 #include "undo.h"
22 #include "object_ops.h"
23 #include "properties-dialog.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"
30 #include "parent.h"
31
32 #if 0
33 #define DEBUG_PRINTF(args) printf args
34 #else
35 #define DEBUG_PRINTF(args)
36 #endif
37
38 void undo_update_menus(UndoStack *stack);
39
40 static void
transaction_point_pointer(Change * change,Diagram * dia)41 transaction_point_pointer(Change *change, Diagram *dia)
42 {
43 /* Empty function used to track transactionpoints. */
44 }
45
46 static int
is_transactionpoint(Change * change)47 is_transactionpoint(Change *change)
48 {
49 return change->apply == transaction_point_pointer;
50 }
51
52 static Change *
new_transactionpoint(void)53 new_transactionpoint(void)
54 {
55 Change *transaction = g_new0(Change, 1);
56
57 if (transaction) {
58 transaction->apply = transaction_point_pointer;
59 transaction->revert = transaction_point_pointer;
60 transaction->free = NULL;
61 }
62
63 return transaction;
64 }
65
66 UndoStack *
new_undo_stack(Diagram * dia)67 new_undo_stack(Diagram *dia)
68 {
69 UndoStack *stack;
70 Change *transaction;
71
72 stack = g_new(UndoStack, 1);
73 if (stack!=NULL){
74 stack->dia = dia;
75 transaction = new_transactionpoint();
76 transaction->next = transaction->prev = NULL;
77 stack->last_change = transaction;
78 stack->current_change = transaction;
79 stack->last_save = transaction;
80 stack->depth = 0;
81 }
82 return stack;
83 }
84
85 void
undo_destroy(UndoStack * stack)86 undo_destroy(UndoStack *stack)
87 {
88 undo_clear(stack);
89 g_free(stack->current_change); /* Free first transaction point. */
90 g_free(stack);
91 }
92
93 static void
undo_remove_redo_info(UndoStack * stack)94 undo_remove_redo_info(UndoStack *stack)
95 {
96 Change *change;
97 Change *next_change;
98
99 DEBUG_PRINTF(("UNDO: Removing redo info\n"));
100
101 change = stack->current_change->next;
102 stack->current_change->next = NULL;
103 stack->last_change = stack->current_change;
104
105 while (change != NULL) {
106 next_change = change->next;
107 if (change->free)
108 (change->free)(change);
109 g_free(change);
110 change = next_change;
111 }
112 undo_update_menus(stack);
113 }
114
115 void
undo_push_change(UndoStack * stack,Change * change)116 undo_push_change(UndoStack *stack, Change *change)
117 {
118 if (stack->current_change != stack->last_change)
119 undo_remove_redo_info(stack);
120
121 DEBUG_PRINTF(("UNDO: Push new change at %d\n", depth(stack)));
122
123 change->prev = stack->last_change;
124 change->next = NULL;
125 stack->last_change->next = change;
126 stack->last_change = change;
127 stack->current_change = change;
128 undo_update_menus(stack);
129 }
130
131 static void
undo_delete_lowest_transaction(UndoStack * stack)132 undo_delete_lowest_transaction(UndoStack *stack)
133 {
134 Change *change;
135 Change *next_change;
136
137 /* Find the lowest change: */
138 change = stack->current_change;
139 while (change->prev != NULL) {
140 change = change->prev;
141 }
142
143 /* Remove changes from the bottom until (and including)
144 * the first transactionpoint.
145 * Stop if we reach current_change or NULL.
146 */
147 do {
148 if ( (change == NULL) || (change == stack->current_change))
149 break;
150
151 next_change = change->next;
152 DEBUG_PRINTF(("freeing one change from the bottom.\n"));
153 if (change->free)
154 (change->free)(change);
155 g_free(change);
156 change = next_change;
157 } while (!is_transactionpoint(change));
158
159 if (is_transactionpoint(change)) {
160 stack->depth--;
161 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
162 }
163 change->prev = NULL;
164 }
165
166 void
undo_set_transactionpoint(UndoStack * stack)167 undo_set_transactionpoint(UndoStack *stack)
168 {
169 Change *transaction;
170
171 if (is_transactionpoint(stack->current_change))
172 return;
173
174 DEBUG_PRINTF(("UNDO: Push new transactionpoint at %d\n", depth(stack)));
175
176 transaction = new_transactionpoint();
177
178 undo_push_change(stack, transaction);
179 stack->depth++;
180 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
181
182 if (prefs.undo_depth > 0) {
183 while (stack->depth > prefs.undo_depth){
184 undo_delete_lowest_transaction(stack);
185 }
186 }
187 }
188
189 void
undo_revert_to_last_tp(UndoStack * stack)190 undo_revert_to_last_tp(UndoStack *stack)
191 {
192 Change *change;
193 Change *prev_change;
194
195 if (stack->current_change->prev == NULL)
196 return; /* Can't revert first transactionpoint */
197
198 change = stack->current_change;
199 do {
200 prev_change = change->prev;
201 (change->revert)(change, stack->dia);
202 change = prev_change;
203 } while (!is_transactionpoint(change));
204 stack->current_change = change;
205 stack->depth--;
206 undo_update_menus(stack);
207 DEBUG_PRINTF(("Decreasing stack depth to: %d\n", stack->depth));
208 }
209
210 void
undo_apply_to_next_tp(UndoStack * stack)211 undo_apply_to_next_tp(UndoStack *stack)
212 {
213 Change *change;
214 Change *next_change;
215
216 change = stack->current_change;
217
218 if (change->next == NULL)
219 return /* Already at top. */;
220
221 do {
222 next_change = change->next;
223 (change->apply)(change, stack->dia);
224 change = next_change;
225 } while ( (change != NULL) &&
226 (!is_transactionpoint(change)) );
227 if (change == NULL)
228 change = stack->last_change;
229 stack->current_change = change;
230 stack->depth++;
231 undo_update_menus(stack);
232 DEBUG_PRINTF(("Increasing stack depth to: %d\n", stack->depth));
233 }
234
235
236 void
undo_clear(UndoStack * stack)237 undo_clear(UndoStack *stack)
238 {
239 Change *change;
240
241 DEBUG_PRINTF(("undo_clear()\n"));
242
243 change = stack->current_change;
244
245 while (change->prev != NULL) {
246 change = change->prev;
247 }
248
249 stack->current_change = change;
250 stack->depth = 0;
251 undo_remove_redo_info(stack);
252 undo_update_menus(stack);
253 }
254
255 /** Make updates to menus associated with undo.
256 * Currently just changes sensitivity, but should in the future also
257 * include changing the labels.
258 */
259 void
undo_update_menus(UndoStack * stack)260 undo_update_menus(UndoStack *stack)
261 {
262 ddisplay_do_update_menu_sensitivity(ddisplay_active());
263 }
264
265 /** Marks the undo stack at the time of a save.
266 */
267 void
undo_mark_save(UndoStack * stack)268 undo_mark_save(UndoStack *stack)
269 {
270 stack->last_save = stack->current_change;
271 }
272
273 /** Returns true if the diagram is undo-wise in the same state as at
274 * last save, i.e. the current change is the same as it was at last save.
275 */
276 gboolean
undo_is_saved(UndoStack * stack)277 undo_is_saved(UndoStack *stack)
278 {
279 return stack->last_save == stack->current_change;
280 }
281
282 /** Returns TRUE if there is an undo or redo item available in the stack.
283 * If undo is true, returns TRUE if there's something to undo, otherwise
284 * returns TRUE if there's something to redo.
285 */
286 gboolean
undo_available(UndoStack * stack,gboolean undo)287 undo_available(UndoStack *stack, gboolean undo)
288 {
289 if (undo) {
290 return stack->current_change != NULL;
291 } else {
292 return stack->current_change != NULL && stack->current_change->next != NULL;
293 }
294 }
295
296 /** Remove items from the undo stack until we hit an undo item of a given
297 * type (indicated by its apply function). Beware that the items are not
298 * just reverted, but totally removed. This also takes with it all the
299 * changes above current_change.
300 *
301 * @param stack The undo stack to remove items from.
302 * @param type Indicator of undo type to remove: An apply function.
303 * @returns The Change object that stopped the search, if any was found,
304 * or NULL otherwise. In the latter case, the undo stack will be empty.
305 */
306 Change*
undo_remove_to(UndoStack * stack,UndoApplyFunc * type)307 undo_remove_to(UndoStack *stack, UndoApplyFunc *type)
308 {
309 Change *current_change = stack->current_change;
310 if (current_change == NULL)
311 return NULL;
312 while (current_change && current_change->apply != *type) {
313 current_change = current_change->prev;
314 }
315 if (current_change != NULL) {
316 stack->current_change = current_change;
317 undo_remove_redo_info(stack);
318 return current_change;
319 } else {
320 undo_clear(stack);
321 return NULL;
322 }
323 }
324
325
326 /****************************************************************/
327 /****************************************************************/
328 /***************** *********************/
329 /***************** Specific undo functions: *********************/
330 /***************** *********************/
331 /****************************************************************/
332 /****************************************************************/
333
334 /******** Move object list: */
335
336 struct MoveObjectsChange {
337 Change change;
338
339 Point *orig_pos;
340 Point *dest_pos;
341 GList *obj_list;
342 };
343
344 static void
move_objects_apply(struct MoveObjectsChange * change,Diagram * dia)345 move_objects_apply(struct MoveObjectsChange *change, Diagram *dia)
346 {
347 GList *list;
348 int i;
349 DiaObject *obj;
350
351 object_add_updates_list(change->obj_list, dia);
352
353 list = change->obj_list;
354 i=0;
355 while (list != NULL) {
356 obj = (DiaObject *) list->data;
357
358 obj->ops->move(obj, &change->dest_pos[i]);
359
360 list = g_list_next(list); i++;
361 }
362
363 list = change->obj_list;
364 while (list!=NULL) {
365 obj = (DiaObject *) list->data;
366
367 diagram_update_connections_object(dia, obj, TRUE);
368
369 list = g_list_next(list);
370 }
371
372 object_add_updates_list(change->obj_list, dia);
373 }
374
375 static void
move_objects_revert(struct MoveObjectsChange * change,Diagram * dia)376 move_objects_revert(struct MoveObjectsChange *change, Diagram *dia)
377 {
378 GList *list;
379 int i;
380 DiaObject *obj;
381
382 object_add_updates_list(change->obj_list, dia);
383
384 list = change->obj_list;
385 i=0;
386 while (list != NULL) {
387 obj = (DiaObject *) list->data;
388
389 obj->ops->move(obj, &change->orig_pos[i]);
390
391 list = g_list_next(list); i++;
392 }
393
394 list = change->obj_list;
395 while (list!=NULL) {
396 obj = (DiaObject *) list->data;
397
398 diagram_update_connections_object(dia, obj, TRUE);
399
400 list = g_list_next(list);
401 }
402
403 object_add_updates_list(change->obj_list, dia);
404 }
405
406 static void
move_objects_free(struct MoveObjectsChange * change)407 move_objects_free(struct MoveObjectsChange *change)
408 {
409 g_free(change->orig_pos);
410 g_free(change->dest_pos);
411 g_list_free(change->obj_list);
412 }
413
414 extern Change *
undo_move_objects(Diagram * dia,Point * orig_pos,Point * dest_pos,GList * obj_list)415 undo_move_objects(Diagram *dia, Point *orig_pos, Point *dest_pos,
416 GList *obj_list)
417 {
418 struct MoveObjectsChange *change;
419
420 change = g_new0(struct MoveObjectsChange, 1);
421
422 change->change.apply = (UndoApplyFunc) move_objects_apply;
423 change->change.revert = (UndoRevertFunc) move_objects_revert;
424 change->change.free = (UndoFreeFunc) move_objects_free;
425
426 change->orig_pos = orig_pos;
427 change->dest_pos = dest_pos;
428 change->obj_list = obj_list;
429
430 DEBUG_PRINTF(("UNDO: Push new move objects at %d\n", depth(dia->undo)));
431 undo_push_change(dia->undo, (Change *) change);
432 return (Change *)change;
433 }
434
435 /********** Move handle: */
436
437 struct MoveHandleChange {
438 Change change;
439
440 Point orig_pos;
441 Point dest_pos;
442 Handle *handle;
443 DiaObject *obj;
444 };
445
446 static void
move_handle_apply(struct MoveHandleChange * change,Diagram * dia)447 move_handle_apply(struct MoveHandleChange *change, Diagram *dia)
448 {
449 object_add_updates(change->obj, dia);
450 change->obj->ops->move_handle(change->obj, change->handle,
451 &change->dest_pos, NULL,
452 HANDLE_MOVE_USER_FINAL, 0);
453 object_add_updates(change->obj, dia);
454 diagram_update_connections_object(dia, change->obj, TRUE);
455 }
456
457 static void
move_handle_revert(struct MoveHandleChange * change,Diagram * dia)458 move_handle_revert(struct MoveHandleChange *change, Diagram *dia)
459 {
460 object_add_updates(change->obj, dia);
461 change->obj->ops->move_handle(change->obj, change->handle,
462 &change->orig_pos, NULL,
463 HANDLE_MOVE_USER_FINAL, 0);
464 object_add_updates(change->obj, dia);
465 diagram_update_connections_object(dia, change->obj, TRUE);
466 }
467
468 static void
move_handle_free(struct MoveHandleChange * change)469 move_handle_free(struct MoveHandleChange *change)
470 {
471 }
472
473
474 Change *
undo_move_handle(Diagram * dia,Handle * handle,DiaObject * obj,Point orig_pos,Point dest_pos)475 undo_move_handle(Diagram *dia,
476 Handle *handle, DiaObject *obj,
477 Point orig_pos, Point dest_pos)
478 {
479 struct MoveHandleChange *change;
480
481 change = g_new0(struct MoveHandleChange, 1);
482
483 change->change.apply = (UndoApplyFunc) move_handle_apply;
484 change->change.revert = (UndoRevertFunc) move_handle_revert;
485 change->change.free = (UndoFreeFunc) move_handle_free;
486
487 change->orig_pos = orig_pos;
488 change->dest_pos = dest_pos;
489 change->handle = handle;
490 change->obj = obj;
491
492 DEBUG_PRINTF(("UNDO: Push new move handle at %d\n", depth(dia->undo)));
493
494 undo_push_change(dia->undo, (Change *) change);
495 return (Change *)change;
496 }
497
498 /***************** Connect object: */
499
500 struct ConnectChange {
501 Change change;
502
503 DiaObject *obj;
504 Handle *handle;
505 ConnectionPoint *connectionpoint;
506 Point handle_pos;
507 };
508
509 static void
connect_apply(struct ConnectChange * change,Diagram * dia)510 connect_apply(struct ConnectChange *change, Diagram *dia)
511 {
512 object_connect(change->obj, change->handle, change->connectionpoint);
513
514 object_add_updates(change->obj, dia);
515 change->obj->ops->move_handle(change->obj, change->handle ,
516 &change->connectionpoint->pos,
517 change->connectionpoint,
518 HANDLE_MOVE_CONNECTED, 0);
519
520 object_add_updates(change->obj, dia);
521 }
522
523 static void
connect_revert(struct ConnectChange * change,Diagram * dia)524 connect_revert(struct ConnectChange *change, Diagram *dia)
525 {
526 object_unconnect(change->obj, change->handle);
527
528 object_add_updates(change->obj, dia);
529 change->obj->ops->move_handle(change->obj, change->handle ,
530 &change->handle_pos, NULL,
531 HANDLE_MOVE_CONNECTED, 0);
532
533 object_add_updates(change->obj, dia);
534 }
535
536 static void
connect_free(struct ConnectChange * change)537 connect_free(struct ConnectChange *change)
538 {
539 }
540
541 extern Change *
undo_connect(Diagram * dia,DiaObject * obj,Handle * handle,ConnectionPoint * connectionpoint)542 undo_connect(Diagram *dia, DiaObject *obj, Handle *handle,
543 ConnectionPoint *connectionpoint)
544 {
545 struct ConnectChange *change;
546
547 change = g_new0(struct ConnectChange, 1);
548
549 change->change.apply = (UndoApplyFunc) connect_apply;
550 change->change.revert = (UndoRevertFunc) connect_revert;
551 change->change.free = (UndoFreeFunc) connect_free;
552
553 change->obj = obj;
554 change->handle = handle;
555 change->handle_pos = handle->pos;
556 change->connectionpoint = connectionpoint;
557
558 DEBUG_PRINTF(("UNDO: Push new connect at %d\n", depth(dia->undo)));
559 undo_push_change(dia->undo, (Change *) change);
560 return (Change *)change;
561 }
562
563 /*************** Unconnect object: */
564
565 struct UnconnectChange {
566 Change change;
567
568 DiaObject *obj;
569 Handle *handle;
570 ConnectionPoint *connectionpoint;
571 };
572
573 static void
unconnect_apply(struct UnconnectChange * change,Diagram * dia)574 unconnect_apply(struct UnconnectChange *change, Diagram *dia)
575 {
576 object_unconnect(change->obj, change->handle);
577
578 object_add_updates(change->obj, dia);
579 }
580
581 static void
unconnect_revert(struct UnconnectChange * change,Diagram * dia)582 unconnect_revert(struct UnconnectChange *change, Diagram *dia)
583 {
584 object_connect(change->obj, change->handle, change->connectionpoint);
585
586 object_add_updates(change->obj, dia);
587 }
588
589 static void
unconnect_free(struct UnconnectChange * change)590 unconnect_free(struct UnconnectChange *change)
591 {
592 }
593
594 extern Change *
undo_unconnect(Diagram * dia,DiaObject * obj,Handle * handle)595 undo_unconnect(Diagram *dia, DiaObject *obj, Handle *handle)
596 {
597 struct UnconnectChange *change;
598
599 change = g_new0(struct UnconnectChange, 1);
600
601 change->change.apply = (UndoApplyFunc) unconnect_apply;
602 change->change.revert = (UndoRevertFunc) unconnect_revert;
603 change->change.free = (UndoFreeFunc) unconnect_free;
604
605 change->obj = obj;
606 change->handle = handle;
607 change->connectionpoint = handle->connected_to;
608
609 DEBUG_PRINTF(("UNDO: Push new unconnect at %d\n", depth(dia->undo)));
610 undo_push_change(dia->undo, (Change *) change);
611 return (Change *)change;
612 }
613
614
615 /******** Delete object list: */
616
617 struct DeleteObjectsChange {
618 Change change;
619
620 Layer *layer;
621 GList *obj_list; /* Owning reference when applied */
622 GList *original_objects;
623 int applied;
624 };
625
626 static void
delete_objects_apply(struct DeleteObjectsChange * change,Diagram * dia)627 delete_objects_apply(struct DeleteObjectsChange *change, Diagram *dia)
628 {
629 GList *list;
630
631 DEBUG_PRINTF(("delete_objects_apply()\n"));
632 change->applied = 1;
633 diagram_unselect_objects(dia, change->obj_list);
634 layer_remove_objects(change->layer, change->obj_list);
635 object_add_updates_list(change->obj_list, dia);
636
637 list = change->obj_list;
638 while (list != NULL) {
639 DiaObject *obj = (DiaObject *)list->data;
640
641 /* Have to hide any open properties dialog
642 if it contains some object in cut_list */
643 properties_hide_if_shown(dia, obj);
644
645 if (obj->parent) /* Lose references to deleted object */
646 obj->parent->children = g_list_remove(obj->parent->children, obj);
647
648 list = g_list_next(list);
649 }
650
651 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
652 }
653
654 static void
delete_objects_revert(struct DeleteObjectsChange * change,Diagram * dia)655 delete_objects_revert(struct DeleteObjectsChange *change, Diagram *dia)
656 {
657 GList *list;
658 DEBUG_PRINTF(("delete_objects_revert()\n"));
659 change->applied = 0;
660 layer_set_object_list(change->layer,
661 g_list_copy(change->original_objects));
662 object_add_updates_list(change->obj_list, dia);
663
664 list = change->obj_list;
665 while (list)
666 {
667 DiaObject *obj = (DiaObject *) list->data;
668 if (obj->parent) /* Restore child references */
669 obj->parent->children = g_list_append(obj->parent->children, obj);
670
671 /* Emit a signal per object reverted */
672 data_emit(layer_get_parent_diagram(change->layer),change->layer,obj,"object_add");
673
674 list = g_list_next(list);
675 }
676
677 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
678 }
679
680 static void
delete_objects_free(struct DeleteObjectsChange * change)681 delete_objects_free(struct DeleteObjectsChange *change)
682 {
683 DEBUG_PRINTF(("delete_objects_free()\n"));
684 if (change->applied)
685 destroy_object_list(change->obj_list);
686 else
687 g_list_free(change->obj_list);
688 g_list_free(change->original_objects);
689 }
690
691 /*
692 This function deletes specified objects along with any children
693 they might have.
694 undo_delete_objects() only deletes the objects that are specified.
695 */
696 Change *
undo_delete_objects_children(Diagram * dia,GList * obj_list)697 undo_delete_objects_children(Diagram *dia, GList *obj_list)
698 {
699 return undo_delete_objects(dia, parent_list_affected(obj_list));
700 }
701
702 Change *
undo_delete_objects(Diagram * dia,GList * obj_list)703 undo_delete_objects(Diagram *dia, GList *obj_list)
704 {
705 struct DeleteObjectsChange *change;
706
707 change = g_new0(struct DeleteObjectsChange, 1);
708
709 change->change.apply = (UndoApplyFunc) delete_objects_apply;
710 change->change.revert = (UndoRevertFunc) delete_objects_revert;
711 change->change.free = (UndoFreeFunc) delete_objects_free;
712
713 change->layer = dia->data->active_layer;
714 change->obj_list = obj_list;
715 change->original_objects = g_list_copy(dia->data->active_layer->objects);
716 change->applied = 0;
717
718 DEBUG_PRINTF(("UNDO: Push new delete objects at %d\n", depth(dia->undo)));
719 undo_push_change(dia->undo, (Change *) change);
720 return (Change *)change;
721 }
722
723 /******** Insert object list: */
724
725 struct InsertObjectsChange {
726 Change change;
727
728 Layer *layer;
729 GList *obj_list; /* Owning reference when not applied */
730 int applied;
731 };
732
733 static void
insert_objects_apply(struct InsertObjectsChange * change,Diagram * dia)734 insert_objects_apply(struct InsertObjectsChange *change, Diagram *dia)
735 {
736 DEBUG_PRINTF(("insert_objects_apply()\n"));
737 change->applied = 1;
738 layer_add_objects(change->layer, g_list_copy(change->obj_list));
739 object_add_updates_list(change->obj_list, dia);
740 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
741 }
742
743 static void
insert_objects_revert(struct InsertObjectsChange * change,Diagram * dia)744 insert_objects_revert(struct InsertObjectsChange *change, Diagram *dia)
745 {
746 GList *list;
747
748 DEBUG_PRINTF(("insert_objects_revert()\n"));
749 change->applied = 0;
750 diagram_unselect_objects(dia, change->obj_list);
751 layer_remove_objects(change->layer, change->obj_list);
752 object_add_updates_list(change->obj_list, dia);
753
754 list = change->obj_list;
755 while (list != NULL) {
756 DiaObject *obj = (DiaObject *)list->data;
757
758 /* Have to hide any open properties dialog
759 if it contains some object in cut_list */
760 properties_hide_if_shown(dia, obj);
761
762 list = g_list_next(list);
763 }
764
765 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
766 }
767
768 static void
insert_objects_free(struct InsertObjectsChange * change)769 insert_objects_free(struct InsertObjectsChange *change)
770 {
771 DEBUG_PRINTF(("insert_objects_free()\n"));
772 if (!change->applied)
773 destroy_object_list(change->obj_list);
774 else
775 g_list_free(change->obj_list);
776 }
777
778 Change *
undo_insert_objects(Diagram * dia,GList * obj_list,int applied)779 undo_insert_objects(Diagram *dia, GList *obj_list, int applied)
780 {
781 struct InsertObjectsChange *change;
782
783 change = g_new0(struct InsertObjectsChange, 1);
784
785 change->change.apply = (UndoApplyFunc) insert_objects_apply;
786 change->change.revert = (UndoRevertFunc) insert_objects_revert;
787 change->change.free = (UndoFreeFunc) insert_objects_free;
788
789 change->layer = dia->data->active_layer;
790 change->obj_list = obj_list;
791 change->applied = applied;
792
793 DEBUG_PRINTF(("UNDO: Push new insert objects at %d\n", depth(dia->undo)));
794 undo_push_change(dia->undo, (Change *) change);
795 return (Change *)change;
796 }
797
798 /******** Reorder object list: */
799
800 struct ReorderObjectsChange {
801 Change change;
802
803 Layer *layer;
804 GList *changed_list; /* Owning reference when applied */
805 GList *original_objects;
806 GList *reordered_objects;
807 };
808
809 static void
reorder_objects_apply(struct ReorderObjectsChange * change,Diagram * dia)810 reorder_objects_apply(struct ReorderObjectsChange *change, Diagram *dia)
811 {
812 DEBUG_PRINTF(("reorder_objects_apply()\n"));
813 layer_set_object_list(change->layer,
814 g_list_copy(change->reordered_objects));
815 object_add_updates_list(change->changed_list, dia);
816 }
817
818 static void
reorder_objects_revert(struct ReorderObjectsChange * change,Diagram * dia)819 reorder_objects_revert(struct ReorderObjectsChange *change, Diagram *dia)
820 {
821 DEBUG_PRINTF(("reorder_objects_revert()\n"));
822 layer_set_object_list(change->layer,
823 g_list_copy(change->original_objects));
824 object_add_updates_list(change->changed_list, dia);
825 }
826
827 static void
reorder_objects_free(struct ReorderObjectsChange * change)828 reorder_objects_free(struct ReorderObjectsChange *change)
829 {
830 DEBUG_PRINTF(("reorder_objects_free()\n"));
831 g_list_free(change->changed_list);
832 g_list_free(change->original_objects);
833 g_list_free(change->reordered_objects);
834 }
835
836 Change *
undo_reorder_objects(Diagram * dia,GList * changed_list,GList * orig_list)837 undo_reorder_objects(Diagram *dia, GList *changed_list, GList *orig_list)
838 {
839 struct ReorderObjectsChange *change;
840
841 change = g_new0(struct ReorderObjectsChange, 1);
842
843 change->change.apply = (UndoApplyFunc) reorder_objects_apply;
844 change->change.revert = (UndoRevertFunc) reorder_objects_revert;
845 change->change.free = (UndoFreeFunc) reorder_objects_free;
846
847 change->layer = dia->data->active_layer;
848 change->changed_list = changed_list;
849 change->original_objects = orig_list;
850 change->reordered_objects = g_list_copy(dia->data->active_layer->objects);
851
852 DEBUG_PRINTF(("UNDO: Push new reorder objects at %d\n", depth(dia->undo)));
853 undo_push_change(dia->undo, (Change *) change);
854 return (Change *)change;
855 }
856
857 /******** ObjectChange: */
858
859 struct ObjectChangeChange {
860 Change change;
861
862 DiaObject *obj;
863 ObjectChange *obj_change;
864 };
865
866
867 static void
object_change_apply(struct ObjectChangeChange * change,Diagram * dia)868 object_change_apply(struct ObjectChangeChange *change,
869 Diagram *dia)
870 {
871 object_add_updates(change->obj, dia);
872 change->obj_change->apply(change->obj_change, change->obj);
873 { /* Make sure object updates its data: */
874 Point p = change->obj->position;
875 (change->obj->ops->move)(change->obj,&p);
876 }
877 object_add_updates(change->obj, dia);
878
879 diagram_update_connections_object(dia, change->obj, TRUE);
880
881 properties_update_if_shown(dia, change->obj);
882 }
883
884 static void
object_change_revert(struct ObjectChangeChange * change,Diagram * dia)885 object_change_revert(struct ObjectChangeChange *change,
886 Diagram *dia)
887 {
888 object_add_updates(change->obj, dia);
889 change->obj_change->revert(change->obj_change, change->obj);
890 { /* Make sure object updates its data: */
891 Point p = change->obj->position;
892 (change->obj->ops->move)(change->obj,&p);
893 }
894 object_add_updates(change->obj, dia);
895
896 diagram_update_connections_object(dia, change->obj, TRUE);
897
898 properties_update_if_shown(dia, change->obj);
899 }
900
901 static void
object_change_free(struct ObjectChangeChange * change)902 object_change_free(struct ObjectChangeChange *change)
903 {
904 DEBUG_PRINTF(("state_change_free()\n"));
905 if (change->obj_change->free)
906 (*change->obj_change->free)(change->obj_change);
907 g_free(change->obj_change);
908 }
909
910 Change *
undo_object_change(Diagram * dia,DiaObject * obj,ObjectChange * obj_change)911 undo_object_change(Diagram *dia, DiaObject *obj,
912 ObjectChange *obj_change)
913 {
914 struct ObjectChangeChange *change;
915
916 change = g_new0(struct ObjectChangeChange, 1);
917
918 change->change.apply = (UndoApplyFunc) object_change_apply;
919 change->change.revert = (UndoRevertFunc) object_change_revert;
920 change->change.free = (UndoFreeFunc) object_change_free;
921
922 change->obj = obj;
923 change->obj_change = obj_change;
924
925 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
926 undo_push_change(dia->undo, (Change *) change);
927 return (Change *)change;
928 }
929
930 /******** Group object list: */
931
932 /** Grouping and ungrouping are two subtly different changes: While
933 * ungrouping preserves the front/back position, grouping cannot do that,
934 * since the act of grouping destroys positions for the members of the
935 * group, and those positions have to be restored in the undo.
936 */
937
938 struct GroupObjectsChange {
939 Change change;
940
941 Layer *layer;
942 DiaObject *group; /* owning reference if not applied */
943 GList *obj_list; /* The list of objects in this group. Owned by the
944 group */
945 GList *orig_list; /* A copy of the original list of all objects,
946 from before the group was created. Owned by
947 the group */
948 int applied;
949 };
950
951 static void
group_objects_apply(struct GroupObjectsChange * change,Diagram * dia)952 group_objects_apply(struct GroupObjectsChange *change, Diagram *dia)
953 {
954 GList *list;
955
956 DEBUG_PRINTF(("group_objects_apply()\n"));
957
958 change->applied = 1;
959
960 diagram_unselect_objects(dia, change->obj_list);
961 layer_remove_objects(change->layer, change->obj_list);
962 layer_add_object(change->layer, change->group);
963 object_add_updates(change->group, dia);
964
965 list = change->obj_list;
966 while (list != NULL) {
967 DiaObject *obj = (DiaObject *)list->data;
968
969 /* Have to hide any open properties dialog
970 if it contains some object in cut_list */
971 properties_hide_if_shown(dia, obj);
972
973 object_add_updates(obj, dia);
974
975 list = g_list_next(list);
976 }
977
978 diagram_tree_add_object(diagram_tree(), dia, change->group);
979 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
980 }
981
982 static void
group_objects_revert(struct GroupObjectsChange * change,Diagram * dia)983 group_objects_revert(struct GroupObjectsChange *change, Diagram *dia)
984 {
985 DEBUG_PRINTF(("group_objects_revert()\n"));
986 change->applied = 0;
987
988 diagram_unselect_object(dia, change->group);
989 object_add_updates(change->group, dia);
990
991 layer_set_object_list(change->layer, g_list_copy(change->orig_list));
992
993 object_add_updates_list(change->obj_list, dia);
994
995 properties_hide_if_shown(dia, change->group);
996
997 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
998 diagram_tree_remove_object(diagram_tree(), change->group);
999 }
1000
1001 static void
group_objects_free(struct GroupObjectsChange * change)1002 group_objects_free(struct GroupObjectsChange *change)
1003 {
1004 DEBUG_PRINTF(("group_objects_free()\n"));
1005 if (!change->applied) {
1006 group_destroy_shallow(change->group);
1007 change->group = NULL;
1008 change->obj_list = NULL;
1009 /** Leak here? */
1010 }
1011 g_list_free(change->orig_list);
1012 }
1013
1014 Change *
undo_group_objects(Diagram * dia,GList * obj_list,DiaObject * group,GList * orig_list)1015 undo_group_objects(Diagram *dia, GList *obj_list, DiaObject *group,
1016 GList *orig_list)
1017 {
1018 struct GroupObjectsChange *change;
1019
1020 change = g_new0(struct GroupObjectsChange, 1);
1021
1022 change->change.apply = (UndoApplyFunc) group_objects_apply;
1023 change->change.revert = (UndoRevertFunc) group_objects_revert;
1024 change->change.free = (UndoFreeFunc) group_objects_free;
1025
1026 change->layer = dia->data->active_layer;
1027 change->group = group;
1028 change->obj_list = obj_list;
1029 change->orig_list = orig_list;
1030 change->applied = 1;
1031
1032 DEBUG_PRINTF(("UNDO: Push new group objects at %d\n", depth(dia->undo)));
1033 undo_push_change(dia->undo, (Change *) change);
1034 return (Change *)change;
1035 }
1036
1037 /******** Ungroup object list: */
1038
1039 struct UngroupObjectsChange {
1040 Change change;
1041
1042 Layer *layer;
1043 DiaObject *group; /* owning reference if applied */
1044 GList *obj_list; /* This list is owned by the ungroup. */
1045 int group_index;
1046 int applied;
1047 };
1048
1049 static void
ungroup_objects_apply(struct UngroupObjectsChange * change,Diagram * dia)1050 ungroup_objects_apply(struct UngroupObjectsChange *change, Diagram *dia)
1051 {
1052 DEBUG_PRINTF(("ungroup_objects_apply()\n"));
1053
1054 change->applied = 1;
1055
1056 diagram_unselect_object(dia, change->group);
1057 object_add_updates(change->group, dia);
1058 layer_replace_object_with_list(change->layer, change->group,
1059 g_list_copy(change->obj_list));
1060 object_add_updates_list(change->obj_list, dia);
1061
1062 properties_hide_if_shown(dia, change->group);
1063
1064 diagram_tree_add_objects(diagram_tree(), dia, change->obj_list);
1065 diagram_tree_remove_object(diagram_tree(), change->group);
1066 }
1067
1068 static void
ungroup_objects_revert(struct UngroupObjectsChange * change,Diagram * dia)1069 ungroup_objects_revert(struct UngroupObjectsChange *change, Diagram *dia)
1070 {
1071 GList *list;
1072
1073 DEBUG_PRINTF(("ungroup_objects_revert()\n"));
1074 change->applied = 0;
1075
1076
1077 diagram_unselect_objects(dia, change->obj_list);
1078 layer_remove_objects(change->layer, change->obj_list);
1079 layer_add_object_at(change->layer, change->group, change->group_index);
1080 object_add_updates(change->group, dia);
1081
1082 list = change->obj_list;
1083 while (list != NULL) {
1084 DiaObject *obj = (DiaObject *)list->data;
1085
1086 /* Have to hide any open properties dialog
1087 if it contains some object in cut_list */
1088 properties_hide_if_shown(dia, obj);
1089
1090 list = g_list_next(list);
1091 }
1092
1093 diagram_tree_add_object(diagram_tree(), dia, change->group);
1094 diagram_tree_remove_objects(diagram_tree(), change->obj_list);
1095 }
1096
1097 static void
ungroup_objects_free(struct UngroupObjectsChange * change)1098 ungroup_objects_free(struct UngroupObjectsChange *change)
1099 {
1100 DEBUG_PRINTF(("ungroup_objects_free()\n"));
1101 if (change->applied) {
1102 group_destroy_shallow(change->group);
1103 change->group = NULL;
1104 change->obj_list = NULL;
1105 }
1106 }
1107
1108 Change *
undo_ungroup_objects(Diagram * dia,GList * obj_list,DiaObject * group,int group_index)1109 undo_ungroup_objects(Diagram *dia, GList *obj_list, DiaObject *group,
1110 int group_index)
1111 {
1112 struct UngroupObjectsChange *change;
1113
1114 change = g_new0(struct UngroupObjectsChange, 1);
1115
1116 change->change.apply = (UndoApplyFunc) ungroup_objects_apply;
1117 change->change.revert = (UndoRevertFunc) ungroup_objects_revert;
1118 change->change.free = (UndoFreeFunc) ungroup_objects_free;
1119
1120 change->layer = dia->data->active_layer;
1121 change->group = group;
1122 change->obj_list = obj_list;
1123 change->group_index = group_index;
1124 change->applied = 1;
1125
1126 DEBUG_PRINTF(("UNDO: Push new ungroup objects at %d\n", depth(dia->undo)));
1127 undo_push_change(dia->undo, (Change *) change);
1128 return (Change *)change;
1129 }
1130
1131 /******* PARENTING */
1132
1133 struct ParentChange {
1134 Change change;
1135 DiaObject *parentobj, *childobj;
1136 gboolean parent;
1137 };
1138
1139 /** Performs the actual parenting of a child to a parent.
1140 * Since no display changes arise from this, we need call no update. */
1141 static void
parent_object(Diagram * dia,DiaObject * parent,DiaObject * child)1142 parent_object(Diagram *dia, DiaObject *parent, DiaObject *child)
1143 {
1144 child->parent = parent;
1145 parent->children = g_list_prepend(parent->children, child);
1146 }
1147
1148 /** Performs the actual removal of a child from a parent.
1149 * Since no display changes arise from this, we need call no update. */
1150 static void
unparent_object(Diagram * dia,DiaObject * parent,DiaObject * child)1151 unparent_object(Diagram *dia, DiaObject *parent, DiaObject *child)
1152 {
1153 child->parent = NULL;
1154 parent->children = g_list_remove(parent->children, child);
1155 }
1156
1157 /** Applies the given ParentChange */
1158 static void
parent_change_apply(Change * change,Diagram * dia)1159 parent_change_apply(Change *change, Diagram *dia)
1160 {
1161 struct ParentChange *parentchange = (struct ParentChange*)change;
1162 if (parentchange->parent) {
1163 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1164 } else {
1165 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1166 }
1167 }
1168
1169 /** Reverts the given ParentChange */
1170 static void
parent_change_revert(Change * change,Diagram * dia)1171 parent_change_revert(Change *change, Diagram *dia)
1172 {
1173 struct ParentChange *parentchange = (struct ParentChange*)change;
1174 if (!parentchange->parent) {
1175 parent_object(dia, parentchange->parentobj, parentchange->childobj);
1176 } else {
1177 unparent_object(dia, parentchange->parentobj, parentchange->childobj);
1178 }
1179 }
1180
1181 /** Frees items in the change -- none really */
1182 static void
parent_change_free(Change * change)1183 parent_change_free(Change *change)
1184 {
1185 }
1186
1187 /** Create a new Change object for parenting/unparenting.
1188 * `parent' is TRUE if applying this change makes childobj a
1189 * child of parentobj.
1190 */
1191 Change *
undo_parenting(Diagram * dia,DiaObject * parentobj,DiaObject * childobj,gboolean parent)1192 undo_parenting(Diagram *dia, DiaObject* parentobj, DiaObject* childobj,
1193 gboolean parent)
1194 {
1195 struct ParentChange *parentchange = g_new0(struct ParentChange, 1);
1196 Change *change = (Change*)parentchange;
1197 change->apply = parent_change_apply;
1198 change->revert = parent_change_revert;
1199 change->free = parent_change_free;
1200
1201 parentchange->parentobj = parentobj;
1202 parentchange->childobj = childobj;
1203 parentchange->parent = parent;
1204
1205 DEBUG_PRINTF(("UNDO: Push new obj_change at %d\n", depth(dia->undo)));
1206 undo_push_change(dia->undo, change);
1207 return change;
1208 }
1209
1210 /* ********* MOVE TO OTHER LAYER */
1211 typedef struct _MoveObjectToLayerChange {
1212 Change change;
1213 /** The objects we are moving */
1214 GList *objects;
1215 /** All objects in the original layer */
1216 GList *orig_list;
1217 /** The active layer when started */
1218 Layer *orig_layer;
1219 gboolean moving_up;
1220 } MoveObjectToLayerChange;
1221
1222 /*!
1223 * BEWARE: we need to notify the DiagramTree somehow - maybe
1224 * better make it listen to object-add signal?
1225 */
1226 static void
move_object_layer_relative(Diagram * dia,GList * objects,gint dist)1227 move_object_layer_relative(Diagram *dia, GList *objects, gint dist)
1228 {
1229 /* from the active layer to above or below */
1230 Layer *active, *target;
1231 guint pos;
1232
1233 g_return_if_fail(dia->data->active_layer);
1234
1235 active = dia->data->active_layer;
1236 for (pos = 0; pos < dia->data->layers->len; ++pos)
1237 if (active == g_ptr_array_index(dia->data->layers, pos))
1238 break;
1239
1240 pos = (pos + dia->data->layers->len + dist) % dia->data->layers->len;
1241 target = g_ptr_array_index(dia->data->layers, pos);
1242 object_add_updates_list(objects, dia);
1243 layer_remove_objects(active, objects);
1244 diagram_tree_add_objects(diagram_tree(), dia, objects);
1245 layer_add_objects(target, g_list_copy(objects));
1246 data_set_active_layer(dia->data, target);
1247 diagram_tree_add_objects(diagram_tree(), dia, objects);
1248 }
1249
1250 static void
move_object_to_layer_apply(MoveObjectToLayerChange * change,Diagram * dia)1251 move_object_to_layer_apply(MoveObjectToLayerChange *change, Diagram *dia)
1252 {
1253 if (change->moving_up) {
1254 move_object_layer_relative(dia, change->objects, 1);
1255 } else {
1256 move_object_layer_relative(dia, change->objects, -1);
1257 }
1258 }
1259
1260 static void
move_object_to_layer_revert(MoveObjectToLayerChange * change,Diagram * dia)1261 move_object_to_layer_revert(MoveObjectToLayerChange *change, Diagram *dia)
1262 {
1263 if (change->moving_up) {
1264 move_object_layer_relative(dia, change->objects, -1);
1265 } else {
1266 diagram_unselect_objects(dia, change->objects);
1267 move_object_layer_relative(dia, change->objects, 1);
1268 /* overwriting the 'unsorted' list of objects to the order it had before */
1269 layer_set_object_list(change->orig_layer, g_list_copy(change->orig_list));
1270 object_add_updates_list(change->orig_list, dia);
1271 }
1272 }
1273
1274 static void
move_object_to_layer_free(MoveObjectToLayerChange * change)1275 move_object_to_layer_free(MoveObjectToLayerChange *change)
1276 {
1277 g_list_free(change->objects);
1278 g_list_free(change->orig_list);
1279 change->objects = NULL;
1280 change->orig_list = NULL;
1281 }
1282
1283 Change *
undo_move_object_other_layer(Diagram * dia,GList * selected_list,gboolean moving_up)1284 undo_move_object_other_layer(Diagram *dia, GList *selected_list,
1285 gboolean moving_up)
1286 {
1287 MoveObjectToLayerChange *movetolayerchange
1288 = g_new0(MoveObjectToLayerChange, 1);
1289 Change *change = (Change*)movetolayerchange;
1290 change->apply = (UndoApplyFunc) move_object_to_layer_apply;
1291 change->revert = (UndoRevertFunc) move_object_to_layer_revert;
1292 change->free = (UndoFreeFunc) move_object_to_layer_free;
1293
1294 movetolayerchange->orig_layer = dia->data->active_layer;
1295 movetolayerchange->orig_list = g_list_copy(dia->data->active_layer->objects);
1296 movetolayerchange->objects = g_list_copy(selected_list);
1297 movetolayerchange->moving_up = moving_up;
1298
1299 DEBUG_PRINTF(("UNDO: Push new obj_layer_change at %d\n", depth(dia->undo)));
1300 undo_push_change(dia->undo, change);
1301 return change;
1302 }
1303