From 4bd6aac1a27ca10a2bb6432606a43586f3208302 Mon Sep 17 00:00:00 2001
From: Guillem <guillembartrina@gmail.com>
Date: Sat, 20 Jun 2020 12:50:56 +0200
Subject: [PATCH] Partially match pd vanilla and purrdata undo system, ported
 atomic muli-step undo

---
 pd/src/g_undo.c | 145 ++++++++++++++++++++++++++++--------------------
 pd/src/g_undo.h |  22 +++++++-
 2 files changed, 105 insertions(+), 62 deletions(-)

diff --git a/pd/src/g_undo.c b/pd/src/g_undo.c
index 2cedee2f6..450f9dc10 100644
--- a/pd/src/g_undo.c
+++ b/pd/src/g_undo.c
@@ -49,6 +49,9 @@ t_undo_action *canvas_undo_add(t_canvas *x, int type, const char *name,
     void *data)
 {
     //fprintf(stderr,"canvas_undo_add %d\n", type);
+
+    /* Check for empty sequences? */
+
     t_undo_action *a = canvas_undo_init(x);
     a->type = type;
     a->data = (void *)data;
@@ -58,6 +61,30 @@ t_undo_action *canvas_undo_add(t_canvas *x, int type, const char *name,
     return(a);
 }
 
+static void canvas_undo_doit(t_canvas *x, t_undo_action *udo, int action)
+{
+    switch(udo->type)
+    {
+    case UNDO_CONNECT:      canvas_undo_connect(x, udo->data, action); break;     //connect
+    case UNDO_DISCONNECT:   canvas_undo_disconnect(x, udo->data, action); break;  //disconnect
+    case UNDO_CUT:          canvas_undo_cut(x, udo->data, action); break;         //cut
+    case UNDO_MOTION:       canvas_undo_move(x, udo->data, action); break;        //move
+    case UNDO_PASTE:        canvas_undo_paste(x, udo->data, action); break;       //paste
+    case UNDO_APPLY:        canvas_undo_apply(x, udo->data, action); break;       //apply
+    case UNDO_ARRANGE:      canvas_undo_arrange(x, udo->data, action); break;     //arrange
+    case UNDO_CANVAS_APPLY: canvas_undo_canvas_apply(x, udo->data, action); break;//canvas apply
+    case UNDO_CREATE:       canvas_undo_create(x, udo->data, action); break;      //create
+    case UNDO_RECREATE:     canvas_undo_recreate(x, udo->data, action); break;    //recreate
+    case UNDO_FONT:         canvas_undo_font(x, udo->data, action); break;        //font
+            /* undo sequences are handled in canvas_undo_undo resp canvas_undo_redo */
+    case UNDO_SEQUENCE_START: break;                                            //start undo sequence
+    case UNDO_SEQUENCE_END: break;                                              //end undo sequence
+    case UNDO_INIT: /* catch whether is called with a non FREE action */ break; //init
+    default:
+        error("canvas_undo: unsupported command %d", udo->type);
+    }
+}
+
 void canvas_undo_undo(t_canvas *x)
 {
     int dspwas = canvas_suspend_dsp();
@@ -68,22 +95,35 @@ void canvas_undo_undo(t_canvas *x)
         canvas_editmode(x, 1);
         glist_noselect(x);
         canvas_undo_name = x->u_last->name;
-        switch(x->u_last->type)
+
+        if(UNDO_SEQUENCE_END == x->u_last->type)
         {
-            case 1:    canvas_undo_connect(x, x->u_last->data, UNDO_UNDO); break;         //connect
-            case 2:    canvas_undo_disconnect(x, x->u_last->data, UNDO_UNDO); break;     //disconnect
-            case 3:    canvas_undo_cut(x, x->u_last->data, UNDO_UNDO); break;             //cut
-            case 4:    canvas_undo_move(x, x->u_last->data, UNDO_UNDO); break;            //move
-            case 5:    canvas_undo_paste(x, x->u_last->data, UNDO_UNDO); break;        //paste
-            case 6:    canvas_undo_apply(x, x->u_last->data, UNDO_UNDO); break;        //apply
-            case 7:    canvas_undo_arrange(x, x->u_last->data, UNDO_UNDO); break;        //arrange
-            case 8:    canvas_undo_canvas_apply(x, x->u_last->data, UNDO_UNDO); break;    //canvas apply
-            case 9:    canvas_undo_create(x, x->u_last->data, UNDO_UNDO); break;        //create
-            case 10:canvas_undo_recreate(x, x->u_last->data, UNDO_UNDO); break;        //recreate
-            case 11:canvas_undo_font(x, x->u_last->data, UNDO_UNDO); break;            //font
-            default:
-                error("canvas_undo_undo: unsupported undo command %d", x->u_last->type);
+            int sequence_depth = 1;
+            while((x->u_last = x->u_last->prev)
+                  && (UNDO_INIT != x->u_last->type))
+            {
+                switch(x->u_last->type)
+                {
+                case UNDO_SEQUENCE_START:
+                    sequence_depth--;
+                    break;
+                case UNDO_SEQUENCE_END:
+                    sequence_depth++;
+                    break;
+                default:
+                    canvas_undo_doit(x, x->u_last, UNDO_UNDO);
+                }
+                if (sequence_depth < 1)
+                    break;
+            }
+            if (sequence_depth < 0)
+                bug("undo sequence missing end");
+            else if (sequence_depth > 0)
+                bug("undo sequence missing start");
         }
+
+        canvas_undo_doit(x, x->u_last, UNDO_UNDO);
+
         x->u_last = x->u_last->prev;
         char *undo_action = x->u_last->name;
         char *redo_action = x->u_last->next->name;
@@ -116,22 +156,34 @@ void canvas_undo_redo(t_canvas *x)
         canvas_editmode(x, 1);
         glist_noselect(x);
         canvas_undo_name = x->u_last->name;
-        switch(x->u_last->type)
+
+        if(UNDO_SEQUENCE_START == x->u_last->type)
         {
-            case 1:    canvas_undo_connect(x, x->u_last->data, UNDO_REDO); break;         //connect
-            case 2:    canvas_undo_disconnect(x, x->u_last->data, UNDO_REDO); break;     //disconnect
-            case 3:    canvas_undo_cut(x, x->u_last->data, UNDO_REDO); break;             //cut
-            case 4:    canvas_undo_move(x, x->u_last->data, UNDO_REDO); break;            //move
-            case 5:    canvas_undo_paste(x, x->u_last->data, UNDO_REDO); break;        //paste
-            case 6:    canvas_undo_apply(x, x->u_last->data, UNDO_REDO); break;        //apply
-            case 7:    canvas_undo_arrange(x, x->u_last->data, UNDO_REDO); break;        //arrange
-            case 8:    canvas_undo_canvas_apply(x, x->u_last->data, UNDO_REDO); break;    //canvas apply
-            case 9:    canvas_undo_create(x, x->u_last->data, UNDO_REDO); break;        //create
-            case 10:canvas_undo_recreate(x, x->u_last->data, UNDO_REDO); break;        //recreate
-            case 11:canvas_undo_font(x, x->u_last->data, UNDO_REDO); break;            //font
-            default:
-                error("canvas_undo_redo: unsupported redo command %d", x->u_last->type);
+            int sequence_depth = 1;
+            while(x->u_last->next && (x->u_last = x->u_last->next))
+            {
+                switch(x->u_last->type)
+                {
+                case UNDO_SEQUENCE_END:
+                    sequence_depth--;
+                    break;
+                case UNDO_SEQUENCE_START:
+                    sequence_depth++;
+                    break;
+                default:
+                    canvas_undo_doit(x, x->u_last, UNDO_REDO);
+                }
+                if (sequence_depth < 1)
+                    break;
+            }
+            if (sequence_depth < 0)
+                bug("undo sequence end without start");
+            else if (sequence_depth > 0)
+                bug("undo sequence start without end");
         }
+
+        canvas_undo_doit(x, x->u_last, UNDO_REDO);
+
         char *undo_action = x->u_last->name;
         char *redo_action = (x->u_last->next ? x->u_last->next->name : "no");
         we_are_undoing = 0;
@@ -163,27 +215,14 @@ void canvas_undo_rebranch(t_canvas *x)
         while(a1)
         {
             //fprintf(stderr,".");
-            switch(a1->type)
-            {
-                case 1:    canvas_undo_connect(x, a1->data, UNDO_FREE); break;         //connect
-                case 2:    canvas_undo_disconnect(x, a1->data, UNDO_FREE); break;         //disconnect
-                case 3:    canvas_undo_cut(x, a1->data, UNDO_FREE); break;             //cut
-                case 4:    canvas_undo_move(x, a1->data, UNDO_FREE); break;            //move
-                case 5:    canvas_undo_paste(x, a1->data, UNDO_FREE); break;            //paste
-                case 6:    canvas_undo_apply(x, a1->data, UNDO_FREE); break;            //apply
-                case 7:    canvas_undo_arrange(x, a1->data, UNDO_FREE); break;            //arrange
-                case 8:    canvas_undo_canvas_apply(x, a1->data, UNDO_FREE); break;    //canvas apply
-                case 9:    canvas_undo_create(x, a1->data, UNDO_FREE); break;            //create
-                case 10:canvas_undo_recreate(x, a1->data, UNDO_FREE); break;        //recreate
-                case 11:canvas_undo_font(x, a1->data, UNDO_FREE); break;            //font
-                default:
-                    error("canvas_undo_rebranch: unsupported undo command %d", a1->type);
-            }
+            canvas_undo_doit(x, a1, UNDO_FREE);
             a2 = a1->next;
             freebytes(a1, sizeof(*a1));
             a1 = a2;
         }
+        //x->u_last->next = 0; /* ??? */
     }
+    //gui_vmess("gui_undo_menu", "xss", x, x->u_last->name, "no"); /* ??? */
     canvas_resume_dsp(dspwas);
     //fprintf(stderr,"done!\n");
 }
@@ -211,23 +250,7 @@ void canvas_undo_free(t_canvas *x)
         while(a1)
         {
             //fprintf(stderr,".");
-            switch(a1->type)
-            {
-                case 0: break;                                                        //init
-                case 1:    canvas_undo_connect(x, a1->data, UNDO_FREE); break;         //connect
-                case 2:    canvas_undo_disconnect(x, a1->data, UNDO_FREE); break;         //disconnect
-                case 3:    canvas_undo_cut(x, a1->data, UNDO_FREE); break;             //cut
-                case 4:    canvas_undo_move(x, a1->data, UNDO_FREE); break;            //move
-                case 5:    canvas_undo_paste(x, a1->data, UNDO_FREE); break;            //paste
-                case 6:    canvas_undo_apply(x, a1->data, UNDO_FREE); break;            //apply
-                case 7:    canvas_undo_arrange(x, a1->data, UNDO_FREE); break;            //arrange
-                case 8:    canvas_undo_canvas_apply(x, a1->data, UNDO_FREE); break;    //canvas apply
-                case 9:    canvas_undo_create(x, a1->data, UNDO_FREE); break;            //create
-                case 10:canvas_undo_recreate(x, a1->data, UNDO_FREE); break;        //recreate
-                case 11:canvas_undo_font(x, a1->data, UNDO_FREE); break;            //font
-                default:
-                    error("canvas_undo_free: unsupported undo command %d", a1->type);
-            }
+            canvas_undo_doit(x, a1, UNDO_FREE);
             a2 = a1->next;
             freebytes(a1, sizeof(*a1));
             a1 = a2;
diff --git a/pd/src/g_undo.h b/pd/src/g_undo.h
index f6cfd4bcf..8abdd1943 100644
--- a/pd/src/g_undo.h
+++ b/pd/src/g_undo.h
@@ -39,10 +39,30 @@ Types of undo data:
 10 - recreate
 */
 
+typedef enum
+{
+    UNDO_INIT = 0,
+    UNDO_CONNECT,      /* 1 */
+    UNDO_DISCONNECT,   /* 2 */
+    UNDO_CUT,          /* 3 */
+    UNDO_MOTION,       /* 4 */
+    UNDO_PASTE,        /* 5 */
+    UNDO_APPLY,        /* 6 */
+    UNDO_ARRANGE,      /* 7 */
+    UNDO_CANVAS_APPLY, /* 8 */
+    UNDO_CREATE,       /* 9 */
+    UNDO_RECREATE,     /* 10 */
+    UNDO_FONT,         /* 11 */
+    UNDO_SEQUENCE_START, /* 12 start an atomic sequence of undo actions*/
+    UNDO_SEQUENCE_END,   /* 13 end an atomic sequence of undo actions */
+
+    UNDO_LAST
+} t_undo_type;
+
 struct _undo_action
 {
 	t_canvas *x;				/* canvas undo is associated with */
-	int type;					/* defines what kind of data container it is */
+	t_undo_type type;					/* defines what kind of data container it is */
 	void *data;					/* each action will have a different data container */
 	char *name;					/* name of current action */
 	struct _undo_action *prev;	/* previous undo action */
-- 
GitLab