ui/motif/text.c

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

mercurial