| 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]; |