ui/motif/text.c

changeset 455
391d8c7223d1
parent 452
a0620cf552a6
child 459
4ea4bb379273
equal deleted inserted replaced
454:57a2c6c04966 455:391d8c7223d1
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2014 Olaf Wintermann. All rights reserved. 4 * Copyright 2025 Olaf Wintermann. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
34 #include "container.h" 34 #include "container.h"
35 35
36 #include <cx/string.h> 36 #include <cx/string.h>
37 37
38 38
39 /* ------------------------------ Text Area ------------------------------ */
40
41 UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) {
42 Arg xargs[16];
43 int n = 0;
44
45 XtSetArg(xargs[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
46
47 UiContainerPrivate *ctn = ui_obj_container(obj);
48 UI_APPLY_LAYOUT(ctn->layout, args);
49
50 Widget parent = ctn->prepare(ctn, xargs, &n);
51 char *name = args.name ? (char*)args.name : "textarea";
52 Widget widget = XmCreateScrolledText(parent, name, xargs, n);
53 XtManageChild(widget);
54
55 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_TEXT);
56
57 UiTextArea *textarea = malloc(sizeof(UiTextArea));
58 memset(textarea, 0, sizeof(UiTextArea));
59 textarea->obj = obj;
60 textarea->var = var;
61
62 if(var) {
63 UiText *value = var->value;
64 if(value->value.ptr) {
65 XmTextSetString(widget, value->value.ptr);
66 value->value.free(value->value.ptr);
67 value->value.ptr = NULL;
68 }
69
70 value->set = ui_textarea_set;
71 value->get = ui_textarea_get;
72 value->getsubstr = ui_textarea_getsubstr;
73 value->insert = ui_textarea_insert;
74 value->setposition = ui_textarea_setposition;
75 value->position = ui_textarea_position;
76 value->selection = ui_textarea_selection;
77 value->length = ui_textarea_length;
78 value->value.ptr = NULL;
79 value->obj = widget;
80
81 if(!value->undomgr) {
82 value->undomgr = ui_create_undomgr();
83 }
84
85 XtAddCallback(
86 widget,
87 XmNmodifyVerifyCallback,
88 (XtCallbackProc)ui_text_modify_callback,
89 var);
90 }
91
92 return widget;
93 }
94
95 char* ui_textarea_get(UiText *text) {
96 if(text->value.ptr) {
97 text->value.free(text->value.ptr);
98 }
99 char *str = XmTextGetString(text->obj);
100 text->value.ptr = str;
101 text->value.free = (ui_freefunc)XtFree;
102 return str;
103 }
104
105 void ui_textarea_set(UiText *text, const char *str) {
106 XmTextSetString(text->obj, (char*)str);
107 if(text->value.ptr) {
108 text->value.free(text->value.ptr);
109 }
110 text->value.ptr = NULL;
111 }
112
113 char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
114 if(text->value.ptr) {
115 text->value.free(text->value.ptr);
116 }
117 int length = end - begin;
118 char *str = XtMalloc(length + 1);
119 XmTextGetSubstring(text->obj, begin, length, length + 1, str);
120 text->value.ptr = str;
121 text->value.free = (ui_freefunc)XtFree;
122 return str;
123 }
124
125 void ui_textarea_insert(UiText *text, int pos, char *str) {
126 text->value.ptr = NULL;
127 XmTextInsert(text->obj, pos, str);
128 if(text->value.ptr) {
129 text->value.free(text->value.ptr);
130 }
131 }
132
133 void ui_textarea_setposition(UiText *text, int pos) {
134 XmTextSetInsertionPosition(text->obj, pos);
135 }
136
137 int ui_textarea_position(UiText *text) {
138 long begin;
139 long end;
140 XmTextGetSelectionPosition(text->obj, &begin, &end);
141 text->pos = begin;
142 return text->pos;
143 }
144
145 void ui_textarea_selection(UiText *text, int *begin, int *end) {
146 XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end);
147 }
148
149 int ui_textarea_length(UiText *text) {
150 return (int)XmTextGetLastPosition(text->obj);
151 }
152
153
154
155 UiUndoMgr* ui_create_undomgr() {
156 UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
157 mgr->begin = NULL;
158 mgr->end = NULL;
159 mgr->cur = NULL;
160 mgr->length = 0;
161 mgr->event = 1;
162 return mgr;
163 }
164
165 void ui_destroy_undomgr(UiUndoMgr *mgr) {
166 UiTextBufOp *op = mgr->begin;
167 while(op) {
168 UiTextBufOp *nextOp = op->next;
169 if(op->text) {
170 free(op->text);
171 }
172 free(op);
173 op = nextOp;
174 }
175 free(mgr);
176 }
177
178 void ui_text_selection_callback(
179 Widget widget,
180 UiTextArea *textarea,
181 XtPointer data)
182 {
183 long left = 0;
184 long right = 0;
185 XmTextGetSelectionPosition(widget, &left, &right);
186 int sel = left < right ? 1 : 0;
187 if(sel != textarea->last_selection_state) {
188 if(sel) {
189 ui_set_group(textarea->obj->ctx, UI_GROUP_SELECTION);
190 } else {
191 ui_unset_group(textarea->obj->ctx, UI_GROUP_SELECTION);
192 }
193 }
194 textarea->last_selection_state = sel;
195 }
196
197 void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) {
198 UiText *value = var->value;
199 if(!value->obj) {
200 // TODO: bug, fix
201 return;
202 }
203 if(!value->undomgr) {
204 value->undomgr = ui_create_undomgr();
205 }
206
207 XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
208 int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
209 UiUndoMgr *mgr = value->undomgr;
210 if(!mgr->event) {
211 return;
212 }
213
214 char *text = txv->text->ptr;
215 int length = txv->text->length;
216
217 if(mgr->cur) {
218 UiTextBufOp *elm = mgr->cur->next;
219 if(elm) {
220 mgr->cur->next = NULL;
221 mgr->end = mgr->cur;
222 while(elm) {
223 elm->prev = NULL;
224 UiTextBufOp *next = elm->next;
225 ui_free_textbuf_op(elm);
226 elm = next;
227 }
228 }
229
230 UiTextBufOp *last_op = mgr->cur;
231 if(
232 last_op->type == UI_TEXTBUF_INSERT &&
233 ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
234 {
235 // append text to last op
236 int ln = last_op->len;
237 char *newtext = malloc(ln + length + 1);
238 memcpy(newtext, last_op->text, ln);
239 memcpy(newtext+ln, text, length);
240 newtext[ln+length] = '\0';
241
242 last_op->text = newtext;
243 last_op->len = ln + length;
244 last_op->end += length;
245
246 return;
247 }
248 }
249
250 char *str;
251 if(type == UI_TEXTBUF_INSERT) {
252 str = malloc(length + 1);
253 memcpy(str, text, length);
254 str[length] = 0;
255 } else {
256 length = txv->endPos - txv->startPos;
257 str = malloc(length + 1);
258 XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str);
259 }
260
261 UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
262 op->prev = NULL;
263 op->next = NULL;
264 op->type = type;
265 op->start = txv->startPos;
266 op->end = txv->endPos + 1;
267 op->len = length;
268 op->text = str;
269
270 cx_linked_list_add(
271 (void**)&mgr->begin,
272 (void**)&mgr->end,
273 offsetof(UiTextBufOp, prev),
274 offsetof(UiTextBufOp, next),
275 op);
276
277 mgr->cur = op;
278 }
279
280 int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
281 // return 1 if oldstr + newstr are one word
282
283 int has_space = 0;
284 for(int i=0;i<oldlen;i++) {
285 if(oldstr[i] < 33) {
286 has_space = 1;
287 break;
288 }
289 }
290
291 for(int i=0;i<newlen;i++) {
292 if(has_space && newstr[i] > 32) {
293 return 1;
294 }
295 }
296
297 return 0;
298 }
299
300 void ui_free_textbuf_op(UiTextBufOp *op) {
301 if(op->text) {
302 free(op->text);
303 }
304 free(op);
305 }
306
307
308 void ui_text_undo(UiText *value) {
309 UiUndoMgr *mgr = value->undomgr;
310
311 if(mgr->cur) {
312 UiTextBufOp *op = mgr->cur;
313 mgr->event = 0;
314 switch(op->type) {
315 case UI_TEXTBUF_INSERT: {
316 XmTextReplace(value->obj, op->start, op->end, "");
317 break;
318 }
319 case UI_TEXTBUF_DELETE: {
320 XmTextInsert(value->obj, op->start, op->text);
321 break;
322 }
323 }
324 mgr->event = 1;
325 mgr->cur = mgr->cur->prev;
326 }
327 }
328
329 void ui_text_redo(UiText *value) {
330 UiUndoMgr *mgr = value->undomgr;
331
332 UiTextBufOp *elm = NULL;
333 if(mgr->cur) {
334 if(mgr->cur->next) {
335 elm = mgr->cur->next;
336 }
337 } else if(mgr->begin) {
338 elm = mgr->begin;
339 }
340
341 if(elm) {
342 UiTextBufOp *op = elm;
343 mgr->event = 0;
344 switch(op->type) {
345 case UI_TEXTBUF_INSERT: {
346 XmTextInsert(value->obj, op->start, op->text);
347 break;
348 }
349 case UI_TEXTBUF_DELETE: {
350 XmTextReplace(value->obj, op->start, op->end, "");
351 break;
352 }
353 }
354 mgr->event = 1;
355 mgr->cur = elm;
356 }
357 }
358
359
39 360
40 /* ------------------------------ Text Field ------------------------------ */ 361 /* ------------------------------ Text Field ------------------------------ */
41 362
42 static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs args, int frameless, int password) { 363 static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs args, int frameless, int password) {
43 Arg xargs[16]; 364 Arg xargs[16];

mercurial