15818
|
1 #include <ctype.h>
|
|
2 #include <string.h>
|
|
3
|
|
4 #include "gntbox.h"
|
|
5 #include "gntentry.h"
|
|
6 #include "gntstyle.h"
|
|
7 #include "gnttree.h"
|
|
8 #include "gntutils.h"
|
|
9
|
|
10 enum
|
|
11 {
|
|
12 SIG_TEXT_CHANGED,
|
|
13 SIGS,
|
|
14 };
|
|
15 static guint signals[SIGS] = { 0 };
|
|
16
|
|
17 static GntWidgetClass *parent_class = NULL;
|
|
18
|
|
19 static void gnt_entry_set_text_internal(GntEntry *entry, const char *text);
|
|
20
|
|
21 static void
|
|
22 destroy_suggest(GntEntry *entry)
|
|
23 {
|
|
24 if (entry->ddown)
|
|
25 {
|
|
26 gnt_widget_destroy(entry->ddown->parent);
|
|
27 entry->ddown = NULL;
|
|
28 }
|
|
29 }
|
|
30
|
|
31 static char *
|
|
32 get_beginning_of_word(GntEntry *entry)
|
|
33 {
|
|
34 char *s = entry->cursor;
|
|
35 while (s > entry->start)
|
|
36 {
|
|
37 char *t = g_utf8_find_prev_char(entry->start, s);
|
|
38 if (isspace(*t))
|
|
39 break;
|
|
40 s = t;
|
|
41 }
|
|
42 return s;
|
|
43 }
|
|
44
|
|
45 static gboolean
|
|
46 show_suggest_dropdown(GntEntry *entry)
|
|
47 {
|
|
48 char *suggest = NULL;
|
|
49 int len;
|
|
50 int offset = 0, x, y;
|
|
51 int count = 0;
|
|
52 GList *iter;
|
|
53
|
|
54 if (entry->word)
|
|
55 {
|
|
56 char *s = get_beginning_of_word(entry);
|
|
57 suggest = g_strndup(s, entry->cursor - s);
|
|
58 if (entry->scroll < s)
|
|
59 offset = gnt_util_onscreen_width(entry->scroll, s);
|
|
60 }
|
|
61 else
|
|
62 suggest = g_strdup(entry->start);
|
|
63 len = strlen(suggest); /* Don't need to use the utf8-function here */
|
|
64
|
|
65 if (entry->ddown == NULL)
|
|
66 {
|
|
67 GntWidget *box = gnt_vbox_new(FALSE);
|
|
68 entry->ddown = gnt_tree_new();
|
|
69 gnt_tree_set_compare_func(GNT_TREE(entry->ddown), (GCompareFunc)g_utf8_collate);
|
|
70 gnt_box_add_widget(GNT_BOX(box), entry->ddown);
|
|
71 /* XXX: Connect to the "activate" signal for the dropdown tree */
|
|
72
|
|
73 GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT);
|
|
74
|
|
75 gnt_widget_get_position(GNT_WIDGET(entry), &x, &y);
|
|
76 x += offset;
|
|
77 y++;
|
|
78 if (y + 10 >= getmaxy(stdscr))
|
|
79 y -= 11;
|
|
80 gnt_widget_set_position(box, x, y);
|
|
81 }
|
|
82 else
|
|
83 gnt_tree_remove_all(GNT_TREE(entry->ddown));
|
|
84
|
|
85 for (count = 0, iter = entry->suggests; iter; iter = iter->next)
|
|
86 {
|
|
87 const char *text = iter->data;
|
|
88 if (g_ascii_strncasecmp(suggest, text, len) == 0 && strlen(text) >= len)
|
|
89 {
|
|
90 gnt_tree_add_row_after(GNT_TREE(entry->ddown), (gpointer)text,
|
|
91 gnt_tree_create_row(GNT_TREE(entry->ddown), text),
|
|
92 NULL, NULL);
|
|
93 count++;
|
|
94 }
|
|
95 }
|
|
96 g_free(suggest);
|
|
97
|
|
98 if (count == 0)
|
|
99 {
|
|
100 destroy_suggest(entry);
|
|
101 return FALSE;
|
|
102 }
|
|
103
|
|
104 gnt_widget_draw(entry->ddown->parent);
|
|
105 return TRUE;
|
|
106 }
|
|
107
|
|
108 static void
|
|
109 gnt_entry_draw(GntWidget *widget)
|
|
110 {
|
|
111 GntEntry *entry = GNT_ENTRY(widget);
|
|
112 int stop;
|
|
113 gboolean focus;
|
|
114
|
|
115 if ((focus = gnt_widget_has_focus(widget)))
|
|
116 wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TEXT_NORMAL));
|
|
117 else
|
|
118 wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
|
|
119
|
|
120 if (entry->masked)
|
|
121 {
|
|
122 mvwhline(widget->window, 0, 0, gnt_ascii_only() ? '*' : ACS_BULLET,
|
|
123 g_utf8_pointer_to_offset(entry->scroll, entry->end));
|
|
124 }
|
|
125 else
|
|
126 mvwprintw(widget->window, 0, 0, "%s", entry->scroll);
|
|
127
|
|
128 stop = gnt_util_onscreen_width(entry->scroll, entry->end);
|
|
129 if (stop < widget->priv.width)
|
|
130 whline(widget->window, ENTRY_CHAR, widget->priv.width - stop);
|
|
131
|
|
132 if (focus)
|
|
133 mvwchgat(widget->window, 0, gnt_util_onscreen_width(entry->scroll, entry->cursor),
|
|
134 1, A_REVERSE, GNT_COLOR_TEXT_NORMAL, NULL);
|
|
135
|
|
136 GNTDEBUG;
|
|
137 }
|
|
138
|
|
139 static void
|
|
140 gnt_entry_size_request(GntWidget *widget)
|
|
141 {
|
|
142 if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED))
|
|
143 {
|
|
144 widget->priv.height = 1;
|
|
145 widget->priv.width = 20;
|
|
146 }
|
|
147 }
|
|
148
|
|
149 static void
|
|
150 gnt_entry_map(GntWidget *widget)
|
|
151 {
|
|
152 if (widget->priv.width == 0 || widget->priv.height == 0)
|
|
153 gnt_widget_size_request(widget);
|
|
154 GNTDEBUG;
|
|
155 }
|
|
156
|
|
157 static void
|
|
158 entry_redraw(GntWidget *widget)
|
|
159 {
|
|
160 gnt_entry_draw(widget);
|
|
161 gnt_widget_queue_update(widget);
|
|
162 }
|
|
163
|
|
164 static void
|
|
165 entry_text_changed(GntEntry *entry)
|
|
166 {
|
|
167 g_signal_emit(entry, signals[SIG_TEXT_CHANGED], 0);
|
|
168 }
|
|
169
|
|
170 static gboolean
|
|
171 move_back(GntBindable *bind, GList *null)
|
|
172 {
|
|
173 GntEntry *entry = GNT_ENTRY(bind);
|
|
174 if (entry->cursor <= entry->start)
|
|
175 return FALSE;
|
|
176 entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor);
|
|
177 if (entry->cursor < entry->scroll)
|
|
178 entry->scroll = entry->cursor;
|
|
179 entry_redraw(GNT_WIDGET(entry));
|
|
180 return TRUE;
|
|
181 }
|
|
182
|
|
183 static gboolean
|
|
184 move_forward(GntBindable *bind, GList *list)
|
|
185 {
|
|
186 GntEntry *entry = GNT_ENTRY(bind);
|
|
187 if (entry->cursor >= entry->end)
|
|
188 return FALSE;
|
|
189 entry->cursor = g_utf8_find_next_char(entry->cursor, NULL);
|
|
190 while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
|
|
191 entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
|
|
192 entry_redraw(GNT_WIDGET(entry));
|
|
193 return TRUE;
|
|
194 }
|
|
195
|
|
196 static gboolean
|
|
197 backspace(GntBindable *bind, GList *null)
|
|
198 {
|
|
199 int len;
|
|
200 GntEntry *entry = GNT_ENTRY(bind);
|
|
201
|
|
202 if (entry->cursor <= entry->start)
|
|
203 return TRUE;
|
|
204
|
|
205 len = entry->cursor - g_utf8_find_prev_char(entry->start, entry->cursor);
|
|
206 entry->cursor -= len;
|
|
207 memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor);
|
|
208 entry->end -= len;
|
|
209
|
|
210 if (entry->scroll > entry->start)
|
|
211 entry->scroll = g_utf8_find_prev_char(entry->start, entry->scroll);
|
|
212
|
|
213 entry_redraw(GNT_WIDGET(entry));
|
|
214 if (entry->ddown)
|
|
215 show_suggest_dropdown(entry);
|
|
216 entry_text_changed(entry);
|
|
217 return TRUE;
|
|
218 }
|
|
219
|
|
220 static gboolean
|
|
221 delkey(GntBindable *bind, GList *null)
|
|
222 {
|
|
223 int len;
|
|
224 GntEntry *entry = GNT_ENTRY(bind);
|
|
225
|
|
226 if (entry->cursor >= entry->end)
|
|
227 return FALSE;
|
|
228
|
|
229 len = g_utf8_find_next_char(entry->cursor, NULL) - entry->cursor;
|
|
230 memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor - len + 1);
|
|
231 entry->end -= len;
|
|
232 entry_redraw(GNT_WIDGET(entry));
|
|
233
|
|
234 if (entry->ddown)
|
|
235 show_suggest_dropdown(entry);
|
|
236 entry_text_changed(entry);
|
|
237 return TRUE;
|
|
238 }
|
|
239
|
|
240 static gboolean
|
|
241 move_start(GntBindable *bind, GList *null)
|
|
242 {
|
|
243 GntEntry *entry = GNT_ENTRY(bind);
|
|
244 entry->scroll = entry->cursor = entry->start;
|
|
245 entry_redraw(GNT_WIDGET(entry));
|
|
246 return TRUE;
|
|
247 }
|
|
248
|
|
249 static gboolean
|
|
250 move_end(GntBindable *bind, GList *null)
|
|
251 {
|
|
252 GntEntry *entry = GNT_ENTRY(bind);
|
|
253 entry->cursor = entry->end;
|
|
254 /* This should be better than this */
|
|
255 while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
|
|
256 entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
|
|
257 entry_redraw(GNT_WIDGET(entry));
|
|
258 return TRUE;
|
|
259 }
|
|
260
|
|
261 static gboolean
|
|
262 history_prev(GntBindable *bind, GList *null)
|
|
263 {
|
|
264 GntEntry *entry = GNT_ENTRY(bind);
|
|
265 if (entry->histlength && entry->history->prev)
|
|
266 {
|
|
267 entry->history = entry->history->prev;
|
|
268 gnt_entry_set_text_internal(entry, entry->history->data);
|
|
269 destroy_suggest(entry);
|
|
270 entry_text_changed(entry);
|
|
271
|
|
272 return TRUE;
|
|
273 }
|
|
274 return FALSE;
|
|
275 }
|
|
276
|
|
277 static gboolean
|
|
278 history_next(GntBindable *bind, GList *null)
|
|
279 {
|
|
280 GntEntry *entry = GNT_ENTRY(bind);
|
|
281 if (entry->histlength && entry->history->next)
|
|
282 {
|
|
283 if (entry->history->prev == NULL)
|
|
284 {
|
|
285 /* Save the current contents */
|
|
286 char *text = g_strdup(gnt_entry_get_text(entry));
|
|
287 g_free(entry->history->data);
|
|
288 entry->history->data = text;
|
|
289 }
|
|
290
|
|
291 entry->history = entry->history->next;
|
|
292 gnt_entry_set_text_internal(entry, entry->history->data);
|
|
293 destroy_suggest(entry);
|
|
294 entry_text_changed(entry);
|
|
295
|
|
296 return TRUE;
|
|
297 }
|
|
298 return FALSE;
|
|
299 }
|
|
300
|
|
301 static gboolean
|
|
302 clipboard_paste(GntBindable *bind, GList *n)
|
|
303 {
|
|
304 GntEntry *entry = GNT_ENTRY(bind);
|
|
305 gchar *i, *text, *a, *all;
|
|
306 text = i = gnt_get_clipboard_string();
|
|
307 while (*i != '\0') {
|
|
308 i = g_utf8_next_char(i);
|
|
309 if (*i == '\r' || *i == '\n')
|
|
310 *i = ' ';
|
|
311 }
|
|
312 a = g_strndup(entry->start, entry->cursor - entry->start);
|
|
313 all = g_strconcat(a, text, entry->cursor, NULL);
|
|
314 gnt_entry_set_text_internal(entry, all);
|
|
315 g_free(a);
|
|
316 g_free(text);
|
|
317 g_free(all);
|
|
318 return TRUE;
|
|
319 }
|
|
320
|
|
321 static gboolean
|
|
322 suggest_show(GntBindable *bind, GList *null)
|
|
323 {
|
|
324 return show_suggest_dropdown(GNT_ENTRY(bind));
|
|
325 }
|
|
326
|
|
327 static gboolean
|
|
328 suggest_next(GntBindable *bind, GList *null)
|
|
329 {
|
|
330 GntEntry *entry = GNT_ENTRY(bind);
|
|
331 if (entry->ddown) {
|
|
332 gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down", NULL);
|
|
333 return TRUE;
|
|
334 }
|
|
335 return FALSE;
|
|
336 }
|
|
337
|
|
338 static gboolean
|
|
339 suggest_prev(GntBindable *bind, GList *null)
|
|
340 {
|
|
341 GntEntry *entry = GNT_ENTRY(bind);
|
|
342 if (entry->ddown) {
|
|
343 gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-up", NULL);
|
|
344 return TRUE;
|
|
345 }
|
|
346 return FALSE;
|
|
347 }
|
|
348
|
|
349 static gboolean
|
|
350 del_to_home(GntBindable *bind, GList *null)
|
|
351 {
|
|
352 GntEntry *entry = GNT_ENTRY(bind);
|
|
353 if (entry->cursor <= entry->start)
|
|
354 return TRUE;
|
|
355 memmove(entry->start, entry->cursor, entry->end - entry->cursor);
|
|
356 entry->end -= (entry->cursor - entry->start);
|
|
357 entry->cursor = entry->scroll = entry->start;
|
|
358 memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
|
|
359 entry_redraw(GNT_WIDGET(bind));
|
|
360 entry_text_changed(entry);
|
|
361 return TRUE;
|
|
362 }
|
|
363
|
|
364 static gboolean
|
|
365 del_to_end(GntBindable *bind, GList *null)
|
|
366 {
|
|
367 GntEntry *entry = GNT_ENTRY(bind);
|
|
368 if (entry->end <= entry->cursor)
|
|
369 return TRUE;
|
|
370 entry->end = entry->cursor;
|
|
371 memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
|
|
372 entry_redraw(GNT_WIDGET(bind));
|
|
373 entry_text_changed(entry);
|
|
374 return TRUE;
|
|
375 }
|
|
376
|
|
377 #define SAME(a,b) ((g_unichar_isalpha(a) && g_unichar_isalpha(b)) || \
|
|
378 (g_unichar_isdigit(a) && g_unichar_isdigit(b)) || \
|
|
379 (g_unichar_isspace(a) && g_unichar_isspace(b)) || \
|
|
380 (g_unichar_iswide(a) && g_unichar_iswide(b)))
|
|
381
|
|
382 static const char *
|
|
383 begin_word(const char *text, const char *begin)
|
|
384 {
|
|
385 gunichar ch = 0;
|
|
386 while (text > begin && (!*text || g_unichar_isspace(g_utf8_get_char(text))))
|
|
387 text = g_utf8_find_prev_char(begin, text);
|
|
388 ch = g_utf8_get_char(text);
|
|
389 while ((text = g_utf8_find_prev_char(begin, text)) >= begin) {
|
|
390 gunichar cur = g_utf8_get_char(text);
|
|
391 if (!SAME(ch, cur))
|
|
392 break;
|
|
393 }
|
|
394
|
|
395 return (text ? g_utf8_find_next_char(text, NULL) : begin);
|
|
396 }
|
|
397
|
|
398 static const char *
|
|
399 next_begin_word(const char *text, const char *end)
|
|
400 {
|
|
401 gunichar ch = 0;
|
|
402 ch = g_utf8_get_char(text);
|
|
403 while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
|
|
404 gunichar cur = g_utf8_get_char(text);
|
|
405 if (!SAME(ch, cur))
|
|
406 break;
|
|
407 }
|
|
408
|
|
409 while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
|
|
410 text = g_utf8_find_next_char(text, end);
|
|
411 return (text ? text : end);
|
|
412 }
|
|
413
|
|
414 #undef SAME
|
|
415 static gboolean
|
|
416 move_back_word(GntBindable *bind, GList *null)
|
|
417 {
|
|
418 GntEntry *entry = GNT_ENTRY(bind);
|
|
419 const char *iter = g_utf8_find_prev_char(entry->start, entry->cursor);
|
|
420
|
|
421 if (iter < entry->start)
|
|
422 return TRUE;
|
|
423 iter = begin_word(iter, entry->start);
|
|
424 entry->cursor = (char*)iter;
|
|
425 if (entry->cursor < entry->scroll)
|
|
426 entry->scroll = entry->cursor;
|
|
427 entry_redraw(GNT_WIDGET(bind));
|
|
428 return TRUE;
|
|
429 }
|
|
430
|
|
431 static gboolean
|
|
432 del_prev_word(GntBindable *bind, GList *null)
|
|
433 {
|
|
434 GntWidget *widget = GNT_WIDGET(bind);
|
|
435 GntEntry *entry = GNT_ENTRY(bind);
|
|
436 char *iter = g_utf8_find_prev_char(entry->start, entry->cursor);
|
|
437 int count;
|
|
438
|
|
439 if (iter < entry->start)
|
|
440 return TRUE;
|
|
441 iter = (char*)begin_word(iter, entry->start);
|
|
442 count = entry->cursor - iter;
|
|
443 memmove(iter, entry->cursor, entry->end - entry->cursor);
|
|
444 entry->end -= count;
|
|
445 entry->cursor = iter;
|
|
446 if (entry->cursor <= entry->scroll) {
|
|
447 entry->scroll = entry->cursor - widget->priv.width + 2;
|
|
448 if (entry->scroll < entry->start)
|
|
449 entry->scroll = entry->start;
|
|
450 }
|
|
451 memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
|
|
452 entry_redraw(widget);
|
|
453 entry_text_changed(entry);
|
|
454
|
|
455 return TRUE;
|
|
456 }
|
|
457
|
|
458 static gboolean
|
|
459 move_forward_word(GntBindable *bind, GList *list)
|
|
460 {
|
|
461 GntEntry *entry = GNT_ENTRY(bind);
|
|
462 GntWidget *widget = GNT_WIDGET(bind);
|
|
463 entry->cursor = (char *)next_begin_word(entry->cursor, entry->end);
|
|
464 while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width) {
|
|
465 entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
|
|
466 }
|
|
467 entry_redraw(widget);
|
|
468 return TRUE;
|
|
469 }
|
|
470
|
|
471 static gboolean
|
|
472 delete_forward_word(GntBindable *bind, GList *list)
|
|
473 {
|
|
474 GntEntry *entry = GNT_ENTRY(bind);
|
|
475 GntWidget *widget = GNT_WIDGET(bind);
|
|
476 char *iter = (char *)next_begin_word(entry->cursor, entry->end);
|
|
477 int len = entry->end - iter + 1;
|
|
478 if (len <= 0)
|
|
479 return TRUE;
|
|
480 memmove(entry->cursor, iter, len);
|
|
481 len = iter - entry->cursor;
|
|
482 entry->end -= len;
|
|
483 memset(entry->end, '\0', len);
|
|
484 entry_redraw(widget);
|
|
485 entry_text_changed(entry);
|
|
486 return TRUE;
|
|
487 }
|
|
488
|
|
489 static gboolean
|
|
490 gnt_entry_key_pressed(GntWidget *widget, const char *text)
|
|
491 {
|
|
492 GntEntry *entry = GNT_ENTRY(widget);
|
|
493
|
|
494 if (text[0] == 27)
|
|
495 {
|
|
496 if (text[1] == 0)
|
|
497 {
|
|
498 destroy_suggest(entry);
|
|
499 return TRUE;
|
|
500 }
|
|
501
|
|
502 return FALSE;
|
|
503 }
|
|
504 else
|
|
505 {
|
|
506 if (text[0] == '\t')
|
|
507 {
|
|
508 if (entry->ddown)
|
|
509 destroy_suggest(entry);
|
|
510 else if (entry->suggests)
|
|
511 return show_suggest_dropdown(entry);
|
|
512
|
|
513 return FALSE;
|
|
514 }
|
|
515 else if (text[0] == '\r' && entry->ddown)
|
|
516 {
|
|
517 char *text = g_strdup(gnt_tree_get_selection_data(GNT_TREE(entry->ddown)));
|
|
518 destroy_suggest(entry);
|
|
519 if (entry->word)
|
|
520 {
|
|
521 char *s = get_beginning_of_word(entry);
|
|
522 char *iter = text;
|
|
523 while (*iter && toupper(*s) == toupper(*iter))
|
|
524 {
|
|
525 *s++ = *iter++;
|
|
526 }
|
|
527 gnt_entry_key_pressed(widget, iter);
|
|
528 }
|
|
529 else
|
|
530 {
|
|
531 gnt_entry_set_text_internal(entry, text);
|
|
532 }
|
|
533 g_free(text);
|
|
534 entry_text_changed(entry);
|
|
535 return TRUE;
|
|
536 }
|
|
537
|
|
538 if (!iscntrl(text[0]))
|
|
539 {
|
|
540 const char *str, *next;
|
|
541
|
|
542 for (str = text; *str; str = next)
|
|
543 {
|
|
544 int len;
|
|
545 next = g_utf8_find_next_char(str, NULL);
|
|
546 len = next - str;
|
|
547
|
|
548 /* Valid input? */
|
|
549 /* XXX: Is it necessary to use _unichar_ variants here? */
|
|
550 if (ispunct(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_PUNCT))
|
|
551 continue;
|
|
552 if (isspace(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_SPACE))
|
|
553 continue;
|
|
554 if (isalpha(*str) && !(entry->flag & GNT_ENTRY_FLAG_ALPHA))
|
|
555 continue;
|
|
556 if (isdigit(*str) && !(entry->flag & GNT_ENTRY_FLAG_INT))
|
|
557 continue;
|
|
558
|
|
559 /* Reached the max? */
|
|
560 if (entry->max && g_utf8_pointer_to_offset(entry->start, entry->end) >= entry->max)
|
|
561 continue;
|
|
562
|
|
563 if (entry->end + len - entry->start >= entry->buffer)
|
|
564 {
|
|
565 /* This will cause the buffer to grow */
|
|
566 char *tmp = g_strdup(entry->start);
|
|
567 gnt_entry_set_text_internal(entry, tmp);
|
|
568 g_free(tmp);
|
|
569 }
|
|
570
|
|
571 memmove(entry->cursor + len, entry->cursor, entry->end - entry->cursor + 1);
|
|
572 entry->end += len;
|
|
573
|
|
574 while (str < next)
|
|
575 {
|
|
576 if (*str == '\r' || *str == '\n')
|
|
577 *entry->cursor = ' ';
|
|
578 else
|
|
579 *entry->cursor = *str;
|
|
580 entry->cursor++;
|
|
581 str++;
|
|
582 }
|
|
583
|
|
584 while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width)
|
|
585 entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
|
|
586
|
|
587 if (entry->ddown)
|
|
588 show_suggest_dropdown(entry);
|
|
589 }
|
|
590 entry_redraw(widget);
|
|
591 entry_text_changed(entry);
|
|
592 return TRUE;
|
|
593 }
|
|
594 }
|
|
595
|
|
596 return FALSE;
|
|
597 }
|
|
598
|
|
599 static void
|
|
600 gnt_entry_destroy(GntWidget *widget)
|
|
601 {
|
|
602 GntEntry *entry = GNT_ENTRY(widget);
|
|
603 g_free(entry->start);
|
|
604
|
|
605 if (entry->history)
|
|
606 {
|
|
607 entry->history = g_list_first(entry->history);
|
|
608 g_list_foreach(entry->history, (GFunc)g_free, NULL);
|
|
609 g_list_free(entry->history);
|
|
610 }
|
|
611
|
|
612 if (entry->suggests)
|
|
613 {
|
|
614 g_list_foreach(entry->suggests, (GFunc)g_free, NULL);
|
|
615 g_list_free(entry->suggests);
|
|
616 }
|
|
617
|
|
618 if (entry->ddown)
|
|
619 {
|
|
620 gnt_widget_destroy(entry->ddown->parent);
|
|
621 }
|
|
622 }
|
|
623
|
|
624 static void
|
|
625 gnt_entry_lost_focus(GntWidget *widget)
|
|
626 {
|
|
627 GntEntry *entry = GNT_ENTRY(widget);
|
|
628 destroy_suggest(entry);
|
|
629 entry_redraw(widget);
|
|
630 }
|
|
631
|
|
632 static void
|
|
633 gnt_entry_class_init(GntEntryClass *klass)
|
|
634 {
|
|
635 GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
|
|
636 char s[2] = {erasechar(), 0};
|
|
637
|
|
638 parent_class = GNT_WIDGET_CLASS(klass);
|
|
639 parent_class->destroy = gnt_entry_destroy;
|
|
640 parent_class->draw = gnt_entry_draw;
|
|
641 parent_class->map = gnt_entry_map;
|
|
642 parent_class->size_request = gnt_entry_size_request;
|
|
643 parent_class->key_pressed = gnt_entry_key_pressed;
|
|
644 parent_class->lost_focus = gnt_entry_lost_focus;
|
|
645
|
|
646 signals[SIG_TEXT_CHANGED] =
|
|
647 g_signal_new("text_changed",
|
|
648 G_TYPE_FROM_CLASS(klass),
|
|
649 G_SIGNAL_RUN_LAST,
|
|
650 G_STRUCT_OFFSET(GntEntryClass, text_changed),
|
|
651 NULL, NULL,
|
|
652 g_cclosure_marshal_VOID__VOID,
|
|
653 G_TYPE_NONE, 0);
|
|
654
|
|
655 gnt_bindable_class_register_action(bindable, "cursor-home", move_start,
|
|
656 GNT_KEY_CTRL_A, NULL);
|
|
657 gnt_bindable_register_binding(bindable, "cursor-home", GNT_KEY_HOME, NULL);
|
|
658 gnt_bindable_class_register_action(bindable, "cursor-end", move_end,
|
|
659 GNT_KEY_CTRL_E, NULL);
|
|
660 gnt_bindable_register_binding(bindable, "cursor-end", GNT_KEY_END, NULL);
|
|
661 gnt_bindable_class_register_action(bindable, "delete-prev", backspace,
|
|
662 GNT_KEY_BACKSPACE, NULL);
|
|
663 gnt_bindable_register_binding(bindable, "delete-prev", s, NULL);
|
|
664 gnt_bindable_register_binding(bindable, "delete-prev", GNT_KEY_CTRL_H, NULL);
|
|
665 gnt_bindable_class_register_action(bindable, "delete-next", delkey,
|
|
666 GNT_KEY_DEL, NULL);
|
|
667 gnt_bindable_register_binding(bindable, "delete-next", GNT_KEY_CTRL_D, NULL);
|
|
668 gnt_bindable_class_register_action(bindable, "delete-start", del_to_home,
|
|
669 GNT_KEY_CTRL_U, NULL);
|
|
670 gnt_bindable_class_register_action(bindable, "delete-end", del_to_end,
|
|
671 GNT_KEY_CTRL_K, NULL);
|
|
672 gnt_bindable_class_register_action(bindable, "delete-prev-word", del_prev_word,
|
|
673 GNT_KEY_CTRL_W, NULL);
|
|
674 gnt_bindable_class_register_action(bindable, "cursor-prev-word", move_back_word,
|
|
675 "\033" "b", NULL);
|
|
676 gnt_bindable_class_register_action(bindable, "cursor-prev", move_back,
|
|
677 GNT_KEY_LEFT, NULL);
|
|
678 gnt_bindable_register_binding(bindable, "cursor-prev", GNT_KEY_CTRL_B, NULL);
|
|
679 gnt_bindable_class_register_action(bindable, "cursor-next", move_forward,
|
|
680 GNT_KEY_RIGHT, NULL);
|
|
681 gnt_bindable_register_binding(bindable, "cursor-next", GNT_KEY_CTRL_F, NULL);
|
|
682 gnt_bindable_class_register_action(bindable, "cursor-next-word", move_forward_word,
|
|
683 "\033" "f", NULL);
|
|
684 gnt_bindable_class_register_action(bindable, "delete-next-word", delete_forward_word,
|
|
685 "\033" "d", NULL);
|
|
686 gnt_bindable_class_register_action(bindable, "suggest-show", suggest_show,
|
|
687 "\t", NULL);
|
|
688 gnt_bindable_class_register_action(bindable, "suggest-next", suggest_next,
|
|
689 GNT_KEY_DOWN, NULL);
|
|
690 gnt_bindable_class_register_action(bindable, "suggest-prev", suggest_prev,
|
|
691 GNT_KEY_UP, NULL);
|
|
692 gnt_bindable_class_register_action(bindable, "history-prev", history_prev,
|
|
693 GNT_KEY_CTRL_DOWN, NULL);
|
|
694 gnt_bindable_class_register_action(bindable, "history-next", history_next,
|
|
695 GNT_KEY_CTRL_UP, NULL);
|
|
696 gnt_bindable_class_register_action(bindable, "clipboard-paste", clipboard_paste,
|
|
697 GNT_KEY_CTRL_V, NULL);
|
|
698
|
|
699 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
|
|
700 GNTDEBUG;
|
|
701 }
|
|
702
|
|
703 static void
|
|
704 gnt_entry_init(GTypeInstance *instance, gpointer class)
|
|
705 {
|
|
706 GntWidget *widget = GNT_WIDGET(instance);
|
|
707 GntEntry *entry = GNT_ENTRY(instance);
|
|
708
|
|
709 entry->flag = GNT_ENTRY_FLAG_ALL;
|
|
710 entry->max = 0;
|
|
711
|
|
712 entry->histlength = 0;
|
|
713 entry->history = NULL;
|
|
714
|
|
715 entry->word = TRUE;
|
|
716 entry->always = FALSE;
|
|
717 entry->suggests = NULL;
|
|
718
|
|
719 GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry),
|
|
720 GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS);
|
|
721 GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry), GNT_WIDGET_GROW_X);
|
|
722
|
|
723 widget->priv.minw = 3;
|
|
724 widget->priv.minh = 1;
|
|
725
|
|
726 GNTDEBUG;
|
|
727 }
|
|
728
|
|
729 /******************************************************************************
|
|
730 * GntEntry API
|
|
731 *****************************************************************************/
|
|
732 GType
|
|
733 gnt_entry_get_gtype(void)
|
|
734 {
|
|
735 static GType type = 0;
|
|
736
|
|
737 if(type == 0)
|
|
738 {
|
|
739 static const GTypeInfo info = {
|
|
740 sizeof(GntEntryClass),
|
|
741 NULL, /* base_init */
|
|
742 NULL, /* base_finalize */
|
|
743 (GClassInitFunc)gnt_entry_class_init,
|
|
744 NULL, /* class_finalize */
|
|
745 NULL, /* class_data */
|
|
746 sizeof(GntEntry),
|
|
747 0, /* n_preallocs */
|
|
748 gnt_entry_init, /* instance_init */
|
|
749 NULL /* value_table */
|
|
750 };
|
|
751
|
|
752 type = g_type_register_static(GNT_TYPE_WIDGET,
|
|
753 "GntEntry",
|
|
754 &info, 0);
|
|
755 }
|
|
756
|
|
757 return type;
|
|
758 }
|
|
759
|
|
760 GntWidget *gnt_entry_new(const char *text)
|
|
761 {
|
|
762 GntWidget *widget = g_object_new(GNT_TYPE_ENTRY, NULL);
|
|
763 GntEntry *entry = GNT_ENTRY(widget);
|
|
764
|
|
765 gnt_entry_set_text_internal(entry, text);
|
|
766
|
|
767 return widget;
|
|
768 }
|
|
769
|
|
770 static void
|
|
771 gnt_entry_set_text_internal(GntEntry *entry, const char *text)
|
|
772 {
|
|
773 int len;
|
|
774 int scroll, cursor;
|
|
775
|
|
776 g_free(entry->start);
|
|
777
|
|
778 if (text && text[0])
|
|
779 {
|
|
780 len = strlen(text);
|
|
781 }
|
|
782 else
|
|
783 {
|
|
784 len = 0;
|
|
785 }
|
|
786
|
|
787 entry->buffer = len + 128;
|
|
788
|
|
789 scroll = entry->scroll - entry->start;
|
|
790 cursor = entry->end - entry->cursor;
|
|
791
|
|
792 entry->start = g_new0(char, entry->buffer);
|
|
793 if (text)
|
|
794 snprintf(entry->start, len + 1, "%s", text);
|
|
795 entry->end = entry->start + len;
|
|
796
|
|
797 entry->scroll = entry->start + scroll;
|
|
798 entry->cursor = entry->end - cursor;
|
|
799
|
|
800 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(entry), GNT_WIDGET_MAPPED))
|
|
801 entry_redraw(GNT_WIDGET(entry));
|
|
802 }
|
|
803
|
|
804 void gnt_entry_set_text(GntEntry *entry, const char *text)
|
|
805 {
|
|
806 gboolean changed = TRUE;
|
|
807 if (text == NULL && entry->start == NULL)
|
|
808 changed = FALSE;
|
|
809 if (text && entry->start && g_utf8_collate(text, entry->start) == 0)
|
|
810 changed = FALSE;
|
|
811 gnt_entry_set_text_internal(entry, text);
|
|
812 if (changed)
|
|
813 entry_text_changed(entry);
|
|
814 }
|
|
815
|
|
816 void gnt_entry_set_max(GntEntry *entry, int max)
|
|
817 {
|
|
818 entry->max = max;
|
|
819 }
|
|
820
|
|
821 void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag)
|
|
822 {
|
|
823 entry->flag = flag;
|
|
824 /* XXX: Check the existing string to make sure the flags are respected? */
|
|
825 }
|
|
826
|
|
827 const char *gnt_entry_get_text(GntEntry *entry)
|
|
828 {
|
|
829 return entry->start;
|
|
830 }
|
|
831
|
|
832 void gnt_entry_clear(GntEntry *entry)
|
|
833 {
|
|
834 gnt_entry_set_text_internal(entry, NULL);
|
|
835 entry->scroll = entry->cursor = entry->end = entry->start;
|
|
836 entry_redraw(GNT_WIDGET(entry));
|
|
837 destroy_suggest(entry);
|
|
838 entry_text_changed(entry);
|
|
839 }
|
|
840
|
|
841 void gnt_entry_set_masked(GntEntry *entry, gboolean set)
|
|
842 {
|
|
843 entry->masked = set;
|
|
844 }
|
|
845
|
|
846 void gnt_entry_add_to_history(GntEntry *entry, const char *text)
|
|
847 {
|
|
848 g_return_if_fail(entry->history != NULL); /* Need to set_history_length first */
|
|
849
|
|
850 if (g_list_length(entry->history) >= entry->histlength)
|
|
851 return;
|
|
852
|
|
853 entry->history = g_list_first(entry->history);
|
|
854 g_free(entry->history->data);
|
|
855 entry->history->data = g_strdup(text);
|
|
856 entry->history = g_list_prepend(entry->history, NULL);
|
|
857 }
|
|
858
|
|
859 void gnt_entry_set_history_length(GntEntry *entry, int num)
|
|
860 {
|
|
861 if (num == 0)
|
|
862 {
|
|
863 entry->histlength = num;
|
|
864 if (entry->history)
|
|
865 {
|
|
866 entry->history = g_list_first(entry->history);
|
|
867 g_list_foreach(entry->history, (GFunc)g_free, NULL);
|
|
868 g_list_free(entry->history);
|
|
869 entry->history = NULL;
|
|
870 }
|
|
871 return;
|
|
872 }
|
|
873
|
|
874 if (entry->histlength == 0)
|
|
875 {
|
|
876 entry->histlength = num;
|
|
877 entry->history = g_list_append(NULL, NULL);
|
|
878 return;
|
|
879 }
|
|
880
|
|
881 if (num > 0 && num < entry->histlength)
|
|
882 {
|
|
883 GList *first, *iter;
|
|
884 int index = 0;
|
|
885 for (first = entry->history, index = 0; first->prev; first = first->prev, index++);
|
|
886 while ((iter = g_list_nth(first, num)) != NULL)
|
|
887 {
|
|
888 g_free(iter->data);
|
|
889 first = g_list_delete_link(first, iter);
|
|
890 }
|
|
891 entry->histlength = num;
|
|
892 if (index >= num)
|
|
893 entry->history = g_list_last(first);
|
|
894 return;
|
|
895 }
|
|
896
|
|
897 entry->histlength = num;
|
|
898 }
|
|
899
|
|
900 void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word)
|
|
901 {
|
|
902 entry->word = word;
|
|
903 }
|
|
904
|
|
905 void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always)
|
|
906 {
|
|
907 entry->always = always;
|
|
908 }
|
|
909
|
|
910 void gnt_entry_add_suggest(GntEntry *entry, const char *text)
|
|
911 {
|
|
912 GList *find;
|
|
913
|
|
914 if (!text || !*text)
|
|
915 return;
|
|
916
|
|
917 find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate);
|
|
918 if (find)
|
|
919 return;
|
|
920 entry->suggests = g_list_append(entry->suggests, g_strdup(text));
|
|
921 }
|
|
922
|
|
923 void gnt_entry_remove_suggest(GntEntry *entry, const char *text)
|
|
924 {
|
|
925 GList *find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate);
|
|
926 if (find)
|
|
927 {
|
|
928 g_free(find->data);
|
|
929 entry->suggests = g_list_delete_link(entry->suggests, find);
|
|
930 }
|
|
931 }
|
|
932
|