diff -r 84a541c6e093 -r e70e855cea89 ui/motif/text.c --- 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 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; + } +}