ui/motif/text.c

changeset 0
2483f517c562
child 29
3fc287f06305
equal deleted inserted replaced
-1:000000000000 0:2483f517c562
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2014 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "text.h"
33 #include "container.h"
34
35
36 UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) {
37 UiContainer *ct = uic_get_current_container(obj);
38 int n = 0;
39 Arg args[16];
40
41 //XtSetArg(args[n], XmNeditable, TRUE);
42 //n++;
43 XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);
44 n++;
45
46 Widget parent = ct->prepare(ct, args, &n, TRUE);
47 Widget text_area = XmCreateScrolledText(parent, "text_area", args, n);
48 ct->add(ct, XtParent(text_area));
49 XtManageChild(text_area);
50
51 UiTextArea *uitext = cxMalloc(
52 obj->ctx->allocator,
53 sizeof(UiTextArea));
54 uitext->ctx = obj->ctx;
55 uitext->last_selection_state = 0;
56 XtAddCallback(
57 text_area,
58 XmNmotionVerifyCallback,
59 (XtCallbackProc)ui_text_selection_callback,
60 uitext);
61
62 // bind value
63 if(var->value) {
64 UiText *value = var->value;
65 if(value->value.ptr) {
66 XmTextSetString(text_area, value->value.ptr);
67 value->value.free(value->value.ptr);
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 = text_area;
80
81 if(!value->undomgr) {
82 value->undomgr = ui_create_undomgr();
83 }
84
85 XtAddCallback(
86 text_area,
87 XmNmodifyVerifyCallback,
88 (XtCallbackProc)ui_text_modify_callback,
89 var);
90 }
91
92 return text_area;
93 }
94
95 UIWIDGET ui_textarea(UiObject *obj, UiText *value) {
96 UiVar *var = malloc(sizeof(UiVar));
97 var->value = value;
98 var->type = UI_VAR_SPECIAL;
99 return ui_textarea_var(obj, var);
100 }
101
102 UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) {
103 UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT);
104 if(var) {
105 return ui_textarea_var(obj, var);
106 } else {
107 // TODO: error
108 }
109 return NULL;
110 }
111
112 char* ui_textarea_get(UiText *text) {
113 if(text->value.ptr) {
114 text->value.free(text->value.ptr);
115 }
116 char *str = XmTextGetString(text->obj);
117 text->value.ptr = str;
118 text->value.free = (ui_freefunc)XtFree;
119 return str;
120 }
121
122 void ui_textarea_set(UiText *text, char *str) {
123 XmTextSetString(text->obj, str);
124 if(text->value.ptr) {
125 text->value.free(text->value.ptr);
126 }
127 text->value.ptr = NULL;
128 }
129
130 char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
131 if(text->value.ptr) {
132 text->value.free(text->value.ptr);
133 }
134 int length = end - begin;
135 char *str = XtMalloc(length + 1);
136 XmTextGetSubstring(text->obj, begin, length, length + 1, str);
137 text->value.ptr = str;
138 text->value.free = (ui_freefunc)XtFree;
139 return str;
140 }
141
142 void ui_textarea_insert(UiText *text, int pos, char *str) {
143 text->value.ptr = NULL;
144 XmTextInsert(text->obj, pos, str);
145 if(text->value.ptr) {
146 text->value.free(text->value.ptr);
147 }
148 }
149
150 void ui_textarea_setposition(UiText *text, int pos) {
151 XmTextSetInsertionPosition(text->obj, pos);
152 }
153
154 int ui_textarea_position(UiText *text) {
155 long begin;
156 long end;
157 XmTextGetSelectionPosition(text->obj, &begin, &end);
158 text->pos = begin;
159 return text->pos;
160 }
161
162 void ui_textarea_selection(UiText *text, int *begin, int *end) {
163 XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end);
164 }
165
166 int ui_textarea_length(UiText *text) {
167 return (int)XmTextGetLastPosition(text->obj);
168 }
169
170
171 void ui_text_set(UiText *text, char *str) {
172 if(text->set) {
173 text->set(text, str);
174 } else {
175 if(text->value.ptr) {
176 text->value.free(text->value.ptr);
177 }
178 text->value.ptr = XtNewString(str);
179 text->value.free = (ui_freefunc)XtFree;
180 }
181 }
182
183 char* ui_text_get(UiText *text) {
184 if(text->get) {
185 return text->get(text);
186 } else {
187 return text->value.ptr;
188 }
189 }
190
191
192 UiUndoMgr* ui_create_undomgr() {
193 UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
194 mgr->begin = NULL;
195 mgr->end = NULL;
196 mgr->cur = NULL;
197 mgr->length = 0;
198 mgr->event = 1;
199 return mgr;
200 }
201
202 void ui_destroy_undomgr(UiUndoMgr *mgr) {
203 UiTextBufOp *op = mgr->begin;
204 while(op) {
205 UiTextBufOp *nextOp = op->next;
206 if(op->text) {
207 free(op->text);
208 }
209 free(op);
210 op = nextOp;
211 }
212 free(mgr);
213 }
214
215 void ui_text_selection_callback(
216 Widget widget,
217 UiTextArea *textarea,
218 XtPointer data)
219 {
220 long left = 0;
221 long right = 0;
222 XmTextGetSelectionPosition(widget, &left, &right);
223 int sel = left < right ? 1 : 0;
224 if(sel != textarea->last_selection_state) {
225 if(sel) {
226 ui_set_group(textarea->ctx, UI_GROUP_SELECTION);
227 } else {
228 ui_unset_group(textarea->ctx, UI_GROUP_SELECTION);
229 }
230 }
231 textarea->last_selection_state = sel;
232 }
233
234 void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) {
235 UiText *value = var->value;
236 if(!value->obj) {
237 // TODO: bug, fix
238 return;
239 }
240 if(!value->undomgr) {
241 value->undomgr = ui_create_undomgr();
242 }
243
244 XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
245 int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
246 UiUndoMgr *mgr = value->undomgr;
247 if(!mgr->event) {
248 return;
249 }
250
251 char *text = txv->text->ptr;
252 int length = txv->text->length;
253
254 if(mgr->cur) {
255 UiTextBufOp *elm = mgr->cur->next;
256 if(elm) {
257 mgr->cur->next = NULL;
258 mgr->end = mgr->cur;
259 while(elm) {
260 elm->prev = NULL;
261 UiTextBufOp *next = elm->next;
262 ui_free_textbuf_op(elm);
263 elm = next;
264 }
265 }
266
267 UiTextBufOp *last_op = mgr->cur;
268 if(
269 last_op->type == UI_TEXTBUF_INSERT &&
270 ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
271 {
272 // append text to last op
273 int ln = last_op->len;
274 char *newtext = malloc(ln + length + 1);
275 memcpy(newtext, last_op->text, ln);
276 memcpy(newtext+ln, text, length);
277 newtext[ln+length] = '\0';
278
279 last_op->text = newtext;
280 last_op->len = ln + length;
281 last_op->end += length;
282
283 return;
284 }
285 }
286
287 char *str;
288 if(type == UI_TEXTBUF_INSERT) {
289 str = malloc(length + 1);
290 memcpy(str, text, length);
291 str[length] = 0;
292 } else {
293 length = txv->endPos - txv->startPos;
294 str = malloc(length + 1);
295 XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str);
296 }
297
298 UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
299 op->prev = NULL;
300 op->next = NULL;
301 op->type = type;
302 op->start = txv->startPos;
303 op->end = txv->endPos + 1;
304 op->len = length;
305 op->text = str;
306
307 cx_linked_list_add(
308 (void**)&mgr->begin,
309 (void**)&mgr->end,
310 offsetof(UiTextBufOp, prev),
311 offsetof(UiTextBufOp, next),
312 op);
313
314 mgr->cur = op;
315 }
316
317 int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
318 // return 1 if oldstr + newstr are one word
319
320 int has_space = 0;
321 for(int i=0;i<oldlen;i++) {
322 if(oldstr[i] < 33) {
323 has_space = 1;
324 break;
325 }
326 }
327
328 for(int i=0;i<newlen;i++) {
329 if(has_space && newstr[i] > 32) {
330 return 1;
331 }
332 }
333
334 return 0;
335 }
336
337 void ui_free_textbuf_op(UiTextBufOp *op) {
338 if(op->text) {
339 free(op->text);
340 }
341 free(op);
342 }
343
344
345 void ui_text_undo(UiText *value) {
346 UiUndoMgr *mgr = value->undomgr;
347
348 if(mgr->cur) {
349 UiTextBufOp *op = mgr->cur;
350 mgr->event = 0;
351 switch(op->type) {
352 case UI_TEXTBUF_INSERT: {
353 XmTextReplace(value->obj, op->start, op->end, "");
354 break;
355 }
356 case UI_TEXTBUF_DELETE: {
357 XmTextInsert(value->obj, op->start, op->text);
358 break;
359 }
360 }
361 mgr->event = 1;
362 mgr->cur = mgr->cur->prev;
363 }
364 }
365
366 void ui_text_redo(UiText *value) {
367 UiUndoMgr *mgr = value->undomgr;
368
369 UiTextBufOp *elm = NULL;
370 if(mgr->cur) {
371 if(mgr->cur->next) {
372 elm = mgr->cur->next;
373 }
374 } else if(mgr->begin) {
375 elm = mgr->begin;
376 }
377
378 if(elm) {
379 UiTextBufOp *op = elm;
380 mgr->event = 0;
381 switch(op->type) {
382 case UI_TEXTBUF_INSERT: {
383 XmTextInsert(value->obj, op->start, op->text);
384 break;
385 }
386 case UI_TEXTBUF_DELETE: {
387 XmTextReplace(value->obj, op->start, op->end, "");
388 break;
389 }
390 }
391 mgr->event = 1;
392 mgr->cur = elm;
393 }
394 }
395
396
397 /* ------------------------- textfield ------------------------- */
398
399 static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) {
400 UiContainer *ct = uic_get_current_container(obj);
401 int n = 0;
402 Arg args[16];
403 XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT);
404 n++;
405 if(width > 0) {
406 XtSetArg(args[n], XmNcolumns, width / 2 + 1);
407 n++;
408 }
409 if(frameless) {
410 XtSetArg(args[n], XmNshadowThickness, 0);
411 n++;
412 }
413 if(password) {
414 // TODO
415 }
416
417 Widget parent = ct->prepare(ct, args, &n, FALSE);
418 Widget textfield = XmCreateText(parent, "text_field", args, n);
419 ct->add(ct, textfield);
420 XtManageChild(textfield);
421
422 // bind value
423 if(value) {
424 if(value->value.ptr) {
425 XmTextSetString(textfield, value->value.ptr);
426 value->value.free(value->value.ptr);
427 }
428
429 value->set = ui_textfield_set;
430 value->get = ui_textfield_get;
431 value->value.ptr = NULL;
432 value->obj = textfield;
433 }
434
435 return textfield;
436 }
437
438 static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) {
439 UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING);
440 if(var) {
441 UiString *value = var->value;
442 return ui_textfield(obj, value);
443 } else {
444 // TODO: error
445 }
446 return NULL;
447 }
448
449 UIWIDGET ui_textfield(UiObject *obj, UiString *value) {
450 return create_textfield(obj, 0, FALSE, FALSE, value);
451 }
452
453 UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) {
454 return create_textfield_nv(obj, 0, FALSE, FALSE, varname);
455 }
456
457 UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) {
458 return create_textfield(obj, width, FALSE, FALSE, value);
459 }
460
461 UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) {
462 return create_textfield_nv(obj, width, FALSE, FALSE, varname);
463 }
464
465 UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) {
466 return create_textfield(obj, 0, TRUE, FALSE, value);
467 }
468
469 UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) {
470 return create_textfield_nv(obj, 0, TRUE, FALSE, varname);
471 }
472
473 UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) {
474 return create_textfield(obj, 0, FALSE, TRUE, value);
475 }
476
477 UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) {
478 return create_textfield_nv(obj, 0, FALSE, TRUE, varname);
479 }
480
481 UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) {
482 return create_textfield(obj, width, FALSE, TRUE, value);
483 }
484
485 UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) {
486 return create_textfield_nv(obj, width, FALSE, TRUE, varname);
487 }
488
489
490 char* ui_textfield_get(UiString *str) {
491 if(str->value.ptr) {
492 str->value.free(str->value.ptr);
493 }
494 char *value = XmTextGetString(str->obj);
495 str->value.ptr = value;
496 str->value.free = (ui_freefunc)XtFree;
497 return value;
498 }
499
500 void ui_textfield_set(UiString *str, char *value) {
501 XmTextSetString(str->obj, value);
502 if(str->value.ptr) {
503 str->value.free(str->value.ptr);
504 }
505 str->value.ptr = NULL;
506 }
507

mercurial