added undo and redo for motif

Wed, 26 Mar 2014 22:02:57 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 26 Mar 2014 22:02:57 +0100
changeset 9
e70e855cea89
parent 8
84a541c6e093
child 11
979c0e0ee853

added undo and redo for motif

ui/gtk/text.c file | annotate | diff | comparison | revisions
ui/motif/text.c file | annotate | diff | comparison | revisions
ui/motif/text.h file | annotate | diff | comparison | revisions
--- a/ui/gtk/text.c	Wed Mar 26 15:53:43 2014 +0100
+++ b/ui/gtk/text.c	Wed Mar 26 22:02:57 2014 +0100
@@ -132,15 +132,22 @@
     
     if(mgr->cur) {
         UcxList *elm = mgr->cur->next;
-        while(elm) {
-            ui_free_textbuf_op(elm->data);
-            UcxList *next = elm->next;
-            ucx_list_remove(mgr->begin, elm);
-            elm = next;
+        if(elm) {
+            mgr->cur->next = NULL;
+            while(elm) {
+                elm->prev = NULL;   
+                UcxList *next = elm->next;
+                ui_free_textbuf_op(elm->data);
+                free(elm);
+                elm = next;
+            }
         }
         
         UiTextBufOp *last_op = mgr->cur->data;
-        if(ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) {
+        if(
+            last_op->type == UI_TEXTBUF_INSERT &&
+            ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
+        {
             // append text to last op       
             int ln = last_op->len;
             char *newtext = malloc(ln + length + 1);
@@ -186,11 +193,15 @@
     
     if(mgr->cur) {
         UcxList *elm = mgr->cur->next;
-        while(elm) {
-            ui_free_textbuf_op(elm->data);
-            UcxList *next = elm->next;
-            ucx_list_remove(mgr->begin, elm);
-            elm = next;
+        if(elm) {
+            mgr->cur->next = NULL;
+            while(elm) {
+                elm->prev = NULL;   
+                UcxList *next = elm->next;
+                ui_free_textbuf_op(elm->data);
+                free(elm);
+                elm = next;
+            }
         }
     }
     
@@ -223,7 +234,7 @@
 
 void ui_free_textbuf_op(UiTextBufOp *op) {
     if(op->text) {
-        g_free(op->text);
+        free(op->text);
     }
     free(op);
 }
--- a/ui/motif/text.c	Wed Mar 26 15:53:43 2014 +0100
+++ b/ui/motif/text.c	Wed Mar 26 22:02:57 2014 +0100
@@ -52,6 +52,16 @@
         value->set = ui_textarea_set;
         value->value = NULL;
         value->obj = text_area;
+        
+        if(!value->undomgr) {
+            value->undomgr = ui_create_undomgr();
+        }
+        
+        XtAddCallback(
+                text_area,
+                XmNmodifyVerifyCallback,
+                (XtCallbackProc)ui_text_modify_callback,
+                value);
     }
     
     return text_area;
@@ -74,3 +84,160 @@
     text->value = NULL;
     XmTextSetString(text->obj, str);
 }
+
+UiUndoMgr* ui_create_undomgr() {
+    UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
+    mgr->begin = NULL;
+    mgr->cur = NULL;
+    mgr->length = 0;
+    mgr->event = 1;
+    return mgr;
+}
+
+void ui_text_modify_callback(Widget widget, UiText *value, XtPointer data) {
+    XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
+    int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
+    UiUndoMgr *mgr = value->undomgr;
+    if(!mgr->event) {
+        return;
+    }
+    
+    char *text = txv->text->ptr;
+    int length = txv->text->length;
+    
+    if(mgr->cur) {
+        UcxList *elm = mgr->cur->next;
+        if(elm) {
+            mgr->cur->next = NULL;
+            while(elm) {
+                elm->prev = NULL;   
+                UcxList *next = elm->next;
+                ui_free_textbuf_op(elm->data);
+                free(elm);
+                elm = next;
+            }
+        }
+        
+        if(type == UI_TEXTBUF_INSERT) {
+            UiTextBufOp *last_op = mgr->cur->data;
+            if(
+                last_op->type == UI_TEXTBUF_INSERT &&
+                ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
+            {
+                // append text to last op
+                int ln = last_op->len;
+                char *newtext = malloc(ln + length + 1);
+                memcpy(newtext, last_op->text, ln);
+                memcpy(newtext+ln, text, length);
+                newtext[ln+length] = '\0';
+                
+                last_op->text = newtext;
+                last_op->len = ln + length;
+                last_op->end += length;
+
+                return;
+            }
+        }
+    }
+    
+    char *str;
+    if(type == UI_TEXTBUF_INSERT) {
+        str = malloc(length + 1);
+        memcpy(str, text, length);
+        str[length] = 0;
+    } else {
+        length = txv->endPos - txv->startPos;
+        str = malloc(length + 1);
+        XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str);
+    }
+    
+    UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
+    op->type = type;
+    op->start = txv->startPos;
+    op->end = txv->endPos + 1;
+    op->len = length;
+    op->text = str;
+    
+    UcxList *elm = ucx_list_append(NULL, op);
+    mgr->cur = elm;
+    mgr->begin = ucx_list_concat(mgr->begin, elm);
+}
+
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
+    // return 1 if oldstr + newstr are one word
+    
+    int has_space = 0;
+    for(int i=0;i<oldlen;i++) {
+        if(oldstr[i] < 33) {
+            has_space = 1;
+            break;
+        }
+    }
+    
+    for(int i=0;i<newlen;i++) {
+        if(has_space && newstr[i] > 32) {
+            return 1;
+        }
+    }
+    
+    return 0;
+}
+
+void ui_free_textbuf_op(UiTextBufOp *op) {
+    if(op->text) {
+        free(op->text);
+    }
+    free(op);
+}
+
+
+void ui_text_undo(UiText *value) {
+    UiUndoMgr *mgr = value->undomgr;
+    
+    if(mgr->cur) {
+        UiTextBufOp *op = mgr->cur->data;
+        mgr->event = 0;
+        switch(op->type) {
+            case UI_TEXTBUF_INSERT: {
+                XmTextReplace(value->obj, op->start, op->end, "");
+                break;
+            }
+            case UI_TEXTBUF_DELETE: {
+                XmTextInsert(value->obj, op->start, op->text);
+                break;
+            }
+        }
+        mgr->event = 1;
+        mgr->cur = mgr->cur->prev;
+    }
+}
+
+void ui_text_redo(UiText *value) {
+    UiUndoMgr *mgr = value->undomgr;
+    
+    UcxList *elm = NULL;
+    if(mgr->cur) {
+        if(mgr->cur->next) {
+            elm = mgr->cur->next;
+        }
+    } else if(mgr->begin) {
+        elm = mgr->begin;
+    }
+    
+    if(elm) {
+        UiTextBufOp *op = elm->data;
+        mgr->event = 0;
+        switch(op->type) {
+            case UI_TEXTBUF_INSERT: {
+                XmTextInsert(value->obj, op->start, op->text);
+                break;
+            }
+            case UI_TEXTBUF_DELETE: {
+                XmTextReplace(value->obj, op->start, op->end, "");
+                break;
+            }
+        }
+        mgr->event = 1;
+        mgr->cur = elm;
+    }
+}
--- a/ui/motif/text.h	Wed Mar 26 15:53:43 2014 +0100
+++ b/ui/motif/text.h	Wed Mar 26 22:02:57 2014 +0100
@@ -31,14 +31,36 @@
 
 #include "../ui/text.h"
 #include "toolkit.h"
+#include "../../ucx/list.h"
 
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
+#define UI_TEXTBUF_INSERT 0
+#define UI_TEXTBUF_DELETE 1
+typedef struct UiTextBufOp {
+    int  type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE
+    int  start;
+    int  end;
+    int  len;
+    char *text;
+} UiTextBufOp;
+    
+typedef struct UiUndoMgr {
+    UcxList *begin;
+    UcxList *cur;
+    int     length;
+    int     event;
+} UiUndoMgr;
+    
 char* ui_textarea_get(UiText *text);
 void ui_textarea_set(UiText *text, char *str);
 
+UiUndoMgr* ui_create_undomgr();
+void ui_text_modify_callback(Widget widget, UiText *value, XtPointer data);
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen);
+void ui_free_textbuf_op(UiTextBufOp *op);
 
 #ifdef	__cplusplus
 }

mercurial