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