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