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