#include <stdio.h>
#include <stdlib.h>
#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], 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);
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 {
}
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) {
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)
{
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) {
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;
}
}
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) {
}
Widget parent = ct->prepare(ct, args, &n,
FALSE);
Widget textfield = XmCreateText(parent,
"text_field", args, n);
ct->add(ct, textfield);
XtManageChild(textfield);
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 {
}
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;
}