comparison finch/libgnt/gntentry.c @ 15817:0e3a8505ebbe

renamed gaim-text to finch
author Sean Egan <seanegan@gmail.com>
date Sun, 18 Mar 2007 19:38:15 +0000
parents
children efbced3f38ac
comparison
equal deleted inserted replaced
15816:317e7613e581 15817:0e3a8505ebbe
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