/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2014 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "text.h" #include "container.h" UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) { UiContainer *ct = uic_get_current_container(obj); int n = 0; Arg args[16]; //XtSetArg(args[n], XmNeditable, TRUE); //n++; XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; Widget parent = ct->prepare(ct, args, &n, TRUE); Widget text_area = XmCreateScrolledText(parent, "text_area", args, n); ct->add(ct, XtParent(text_area)); XtManageChild(text_area); UiTextArea *uitext = ucx_mempool_malloc( obj->ctx->mempool, sizeof(UiTextArea)); uitext->ctx = obj->ctx; uitext->last_selection_state = 0; XtAddCallback( text_area, XmNmotionVerifyCallback, (XtCallbackProc)ui_text_selection_callback, uitext); // bind value if(var->value) { UiText *value = var->value; if(value->value.ptr) { XmTextSetString(text_area, value->value.ptr); value->value.free(value->value.ptr); } value->set = ui_textarea_set; value->get = ui_textarea_get; value->getsubstr = ui_textarea_getsubstr; value->insert = ui_textarea_insert; value->setposition = ui_textarea_setposition; value->position = ui_textarea_position; value->selection = ui_textarea_selection; value->length = ui_textarea_length; value->value.ptr = NULL; value->obj = text_area; if(!value->undomgr) { value->undomgr = ui_create_undomgr(); } XtAddCallback( text_area, XmNmodifyVerifyCallback, (XtCallbackProc)ui_text_modify_callback, var); } return text_area; } UIWIDGET ui_textarea(UiObject *obj, UiText *value) { UiVar *var = malloc(sizeof(UiVar)); var->value = value; var->type = UI_VAR_SPECIAL; return ui_textarea_var(obj, var); } UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) { UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT); if(var) { return ui_textarea_var(obj, var); } else { // TODO: error } return NULL; } char* ui_textarea_get(UiText *text) { if(text->value.ptr) { text->value.free(text->value.ptr); } char *str = XmTextGetString(text->obj); text->value.ptr = str; text->value.free = (ui_freefunc)XtFree; return str; } void ui_textarea_set(UiText *text, char *str) { XmTextSetString(text->obj, str); if(text->value.ptr) { text->value.free(text->value.ptr); } text->value.ptr = NULL; } char* ui_textarea_getsubstr(UiText *text, int begin, int end) { if(text->value.ptr) { text->value.free(text->value.ptr); } int length = end - begin; char *str = XtMalloc(length + 1); XmTextGetSubstring(text->obj, begin, length, length + 1, str); text->value.ptr = str; text->value.free = (ui_freefunc)XtFree; return str; } void ui_textarea_insert(UiText *text, int pos, char *str) { text->value.ptr = NULL; XmTextInsert(text->obj, pos, str); if(text->value.ptr) { text->value.free(text->value.ptr); } } void ui_textarea_setposition(UiText *text, int pos) { XmTextSetInsertionPosition(text->obj, pos); } int ui_textarea_position(UiText *text) { long begin; long end; XmTextGetSelectionPosition(text->obj, &begin, &end); text->pos = begin; return text->pos; } void ui_textarea_selection(UiText *text, int *begin, int *end) { XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end); } int ui_textarea_length(UiText *text) { return (int)XmTextGetLastPosition(text->obj); } void ui_text_set(UiText *text, char *str) { if(text->set) { text->set(text, str); } else { if(text->value.ptr) { text->value.free(text->value.ptr); } text->value.ptr = XtNewString(str); text->value.free = (ui_freefunc)XtFree; } } char* ui_text_get(UiText *text) { if(text->get) { return text->get(text); } else { return text->value.ptr; } } 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_selection_callback( Widget widget, UiTextArea *textarea, XtPointer data) { long left = 0; long right = 0; XmTextGetSelectionPosition(widget, &left, &right); int sel = left < right ? 1 : 0; if(sel != textarea->last_selection_state) { if(sel) { ui_set_group(textarea->ctx, UI_GROUP_SELECTION); } else { ui_unset_group(textarea->ctx, UI_GROUP_SELECTION); } } textarea->last_selection_state = sel; } void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) { UiText *value = var->value; if(!value->obj) { // TODO: bug, fix return; } if(!value->undomgr) { value->undomgr = ui_create_undomgr(); } 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; } } /* ------------------------- textfield ------------------------- */ static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) { UiContainer *ct = uic_get_current_container(obj); int n = 0; Arg args[16]; XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT); n++; if(width > 0) { XtSetArg(args[n], XmNcolumns, width / 2 + 1); n++; } if(frameless) { XtSetArg(args[n], XmNshadowThickness, 0); n++; } if(password) { // TODO } Widget parent = ct->prepare(ct, args, &n, FALSE); Widget textfield = XmCreateText(parent, "text_field", args, n); ct->add(ct, textfield); XtManageChild(textfield); // bind value if(value) { if(value->value.ptr) { XmTextSetString(textfield, value->value.ptr); value->value.free(value->value.ptr); } value->set = ui_textfield_set; value->get = ui_textfield_get; value->value.ptr = NULL; value->obj = textfield; } return textfield; } static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) { UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING); if(var) { UiString *value = var->value; return ui_textfield(obj, value); } else { // TODO: error } return NULL; } UIWIDGET ui_textfield(UiObject *obj, UiString *value) { return create_textfield(obj, 0, FALSE, FALSE, value); } UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) { return create_textfield_nv(obj, 0, FALSE, FALSE, varname); } UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) { return create_textfield(obj, width, FALSE, FALSE, value); } UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) { return create_textfield_nv(obj, width, FALSE, FALSE, varname); } UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) { return create_textfield(obj, 0, TRUE, FALSE, value); } UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) { return create_textfield_nv(obj, 0, TRUE, FALSE, varname); } UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) { return create_textfield(obj, 0, FALSE, TRUE, value); } UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) { return create_textfield_nv(obj, 0, FALSE, TRUE, varname); } UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) { return create_textfield(obj, width, FALSE, TRUE, value); } UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) { return create_textfield_nv(obj, width, FALSE, TRUE, varname); } char* ui_textfield_get(UiString *str) { if(str->value.ptr) { str->value.free(str->value.ptr); } char *value = XmTextGetString(str->obj); str->value.ptr = value; str->value.free = (ui_freefunc)XtFree; return value; } void ui_textfield_set(UiString *str, char *value) { XmTextSetString(str->obj, value); if(str->value.ptr) { str->value.free(str->value.ptr); } str->value.ptr = NULL; }