Mercurial > pidgin.yaz
comparison finch/libgnt/gntentry.c @ 15818: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
15817:317e7613e581 | 15818: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 |