changeset 14091:ae4cbed1b309

[gaim-migrate @ 16715] Add support for tab-completion and save-history in GntEntry. Also, the keyboard-commands should now work for Xterms. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 12 Aug 2006 10:27:29 +0000
parents 983fbec46eb0
children 58c9f678b77a
files console/gntconv.c console/libgnt/Makefile.am console/libgnt/gntcombobox.c console/libgnt/gntentry.c console/libgnt/gntentry.h console/libgnt/gntkeys.c console/libgnt/gntkeys.h console/libgnt/gntmain.c console/libgnt/gnttree.c console/libgnt/gnttree.h console/libgnt/gntwidget.c console/libgnt/test/tv.c
diffstat 12 files changed, 466 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntconv.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/gntconv.c	Sat Aug 12 10:27:29 2006 +0000
@@ -123,6 +123,7 @@
 			}
 			g_free(escape);
 		}
+		gnt_entry_add_to_history(GNT_ENTRY(ggconv->entry), text);
 		gnt_entry_clear(GNT_ENTRY(ggconv->entry));
 		return TRUE;
 	}
@@ -198,8 +199,11 @@
 	ggc->entry = gnt_entry_new(NULL);
 	gnt_box_add_widget(GNT_BOX(ggc->window), ggc->entry);
 	gnt_widget_set_name(ggc->entry, "conversation-window-entry");
+	gnt_entry_set_history_length(GNT_ENTRY(ggc->entry), -1);
+	gnt_entry_set_word_suggest(GNT_ENTRY(ggc->entry), TRUE);
+	gnt_entry_set_always_suggest(GNT_ENTRY(ggc->entry), FALSE);
 
-	g_signal_connect(G_OBJECT(ggc->entry), "key_pressed", G_CALLBACK(entry_key_pressed), ggc);
+	g_signal_connect_after(G_OBJECT(ggc->entry), "key_pressed", G_CALLBACK(entry_key_pressed), ggc);
 	g_signal_connect(G_OBJECT(ggc->window), "destroy", G_CALLBACK(closing_window), ggc);
 
 	gnt_widget_set_position(ggc->window, gaim_prefs_get_int(PREF_ROOT "/position/x"),
@@ -315,6 +319,9 @@
 static void
 gg_chat_add_users(GaimConversation *conv, GList *users, gboolean new_arrivals)
 {
+	GGConv *ggc = conv->ui_data;
+	GntEntry *entry = GNT_ENTRY(ggc->entry);
+
 	if (!new_arrivals)
 	{
 		/* Print the list of users in the room */
@@ -335,25 +342,39 @@
 				GAIM_MESSAGE_SYSTEM, time(NULL));
 		g_string_free(string, TRUE);
 	}
-	/* XXX: Add the names for string completion */
+
+	for (; users; users = users->next)
+	{
+		GaimConvChatBuddy *cbuddy = users->data;
+		gnt_entry_add_suggest(entry, cbuddy->name);
+		gnt_entry_add_suggest(entry, cbuddy->alias);
+	}
 }
 
 static void
 gg_chat_rename_user(GaimConversation *conv, const char *old, const char *new_n, const char *new_a)
 {
 	/* XXX: Update the name for string completion */
+	GGConv *ggc = conv->ui_data;
+	GntEntry *entry = GNT_ENTRY(ggc->entry);
+	gnt_entry_remove_suggest(entry, old);
+	gnt_entry_add_suggest(entry, new_n);
+	gnt_entry_add_suggest(entry, new_a);
 }
 
 static void
 gg_chat_remove_user(GaimConversation *conv, GList *list)
 {
 	/* XXX: Remove the name from string completion */
+	GGConv *ggc = conv->ui_data;
+	GntEntry *entry = GNT_ENTRY(ggc->entry);
+	for (; list; list = list->next)
+		gnt_entry_remove_suggest(entry, list->data);
 }
 
 static void
 gg_chat_update_user(GaimConversation *conv, const char *user)
 {
-	/* XXX: This probably will not require updating the string completion  */
 }
 
 static GaimConversationUiOps conv_ui_ops = 
--- a/console/libgnt/Makefile.am	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/Makefile.am	Sat Aug 12 10:27:29 2006 +0000
@@ -11,6 +11,7 @@
 	gntcolors.c \
 	gntcombobox.c \
 	gntentry.c \
+	gntkeys.c \
 	gntlabel.c \
 	gntline.c \
 	gntmarshal.c \
--- a/console/libgnt/gntcombobox.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntcombobox.c	Sat Aug 12 10:27:29 2006 +0000
@@ -79,7 +79,7 @@
 		GntWidget *dd = GNT_COMBO_BOX(widget)->dropdown;
 		gnt_widget_size_request(dd);
 		widget->priv.height = 3;   /* For now, a combobox will have border */
-		widget->priv.width = MIN(10, dd->priv.width);
+		widget->priv.width = MIN(10, dd->priv.width + 4);
 	}
 }
 
--- a/console/libgnt/gntentry.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntentry.c	Sat Aug 12 10:27:29 2006 +0000
@@ -1,7 +1,9 @@
 #include <ctype.h>
 #include <string.h>
 
+#include "gntbox.h"
 #include "gntentry.h"
+#include "gnttree.h"
 
 enum
 {
@@ -12,6 +14,92 @@
 static guint signals[SIGS] = { 0 };
 
 static void
+destroy_suggest(GntEntry *entry)
+{
+	if (entry->ddown)
+	{
+		gnt_widget_destroy(entry->ddown->parent);
+		entry->ddown = NULL;
+	}
+}
+
+static char *
+get_beginning_of_word(GntEntry *entry)
+{
+	char *s = entry->cursor;
+	while (s > entry->start)
+	{
+		char *t = g_utf8_find_prev_char(entry->start, s);
+		if ((*t < 'A' || *t > 'Z') && (*t < 'a' || *t > 'z'))
+			break;
+		s = t;
+	}
+	return s;
+}
+
+static gboolean
+show_suggest_dropdown(GntEntry *entry)
+{
+	char *suggest = NULL;
+	int len;
+	int offset = 0, x, y;
+	int count = 0;
+	GList *iter;
+
+	if (entry->word)
+	{
+		char *s = get_beginning_of_word(entry);
+		suggest = g_strndup(s, entry->cursor - s);
+		if (entry->scroll < s)
+			offset = g_utf8_pointer_to_offset(entry->scroll, s);
+	}
+	else
+		suggest = g_strdup(entry->start);
+	len = strlen(suggest);  /* Don't need to use the utf8-function here */
+	
+	if (entry->ddown == NULL)
+	{
+		GntWidget *box = gnt_vbox_new(FALSE);
+		entry->ddown = gnt_tree_new();
+		gnt_box_add_widget(GNT_BOX(box), entry->ddown);
+
+		GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT);
+
+		gnt_widget_get_position(GNT_WIDGET(entry), &x, &y);
+		x += offset;
+		y++;
+		if (y + 10 >= getmaxy(stdscr))
+			y -= 11;
+		gnt_widget_set_position(box, x, y);
+		
+		gnt_widget_draw(box);
+	}
+	else
+		gnt_tree_remove_all(GNT_TREE(entry->ddown));
+
+	for (count = 0, iter = entry->suggests; iter; iter = iter->next)
+	{
+		const char *text = iter->data;
+		if (strncmp(suggest, text, len) == 0 && strlen(text) >= len)
+		{
+			gnt_tree_add_row_after(GNT_TREE(entry->ddown), (gpointer)text,
+					gnt_tree_create_row(GNT_TREE(entry->ddown), text),
+					NULL, NULL);
+			count++;
+		}
+	}
+	g_free(suggest);
+
+	if (count == 0)
+	{
+		destroy_suggest(entry);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
 gnt_entry_draw(GntWidget *widget)
 {
 	GntEntry *entry = GNT_ENTRY(widget);
@@ -80,6 +168,11 @@
 			memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor - len + 1);
 			entry->end -= len;
 			entry_redraw(widget);
+
+			if (entry->ddown)
+				show_suggest_dropdown(entry);
+
+			return TRUE;
 		}
 		else if (strcmp(text + 1, GNT_KEY_LEFT) == 0 && entry->cursor > entry->start)
 		{
@@ -87,6 +180,8 @@
 			if (entry->cursor < entry->scroll)
 				entry->scroll = entry->cursor;
 			entry_redraw(widget);
+
+			return TRUE;
 		}
 		else if (strcmp(text + 1, GNT_KEY_RIGHT) == 0 && entry->cursor < entry->end)
 		{
@@ -94,15 +189,90 @@
 			if (g_utf8_pointer_to_offset(entry->scroll, entry->cursor) >= widget->priv.width)
 				entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
 			entry_redraw(widget);
+
+			return TRUE;
+		}
+		else if (strcmp(text + 1, GNT_KEY_CTRL_DOWN) == 0 && entry->histlength)
+		{
+			if (entry->history->prev)
+			{
+				entry->history = entry->history->prev;
+				gnt_entry_set_text(entry, entry->history->data);
+				destroy_suggest(entry);
+
+				return TRUE;
+			}
+		}
+		else if (strcmp(text + 1, GNT_KEY_UP) == 0 ||
+				strcmp(text + 1, GNT_KEY_DOWN) == 0)
+		{
+			if (entry->ddown)
+			{
+				gnt_widget_key_pressed(entry->ddown, text);
+				return TRUE;
+			}
+		}
+		else if (strcmp(text + 1, GNT_KEY_CTRL_UP) == 0 && entry->histlength)
+		{
+			if (entry->history->next)
+			{
+				if (entry->history->prev == NULL)
+				{
+					/* Save the current contents */
+					char *text = g_strdup(gnt_entry_get_text(entry));
+					g_free(entry->history->data);
+					entry->history->data = text;
+				}
+
+				entry->history = entry->history->next;
+				gnt_entry_set_text(entry, entry->history->data);
+				destroy_suggest(entry);
+
+				return TRUE;
+			}
 		}
 		/* XXX: handle other keys, like home/end, and ctrl+ goodness */
-		else
-			return FALSE;
+		else if (text[1] == 0)
+		{
+			destroy_suggest(entry);
+		}
 
-		return TRUE;
+		return FALSE;
 	}
 	else
 	{
+		if (text[0] == '\t')
+		{
+			if (entry->ddown)
+				destroy_suggest(entry);
+			else if (entry->suggests)
+				return show_suggest_dropdown(entry);
+
+			return FALSE;
+		}
+		else if (text[0] == '\r' && entry->ddown)
+		{
+			char *text = g_strdup(gnt_tree_get_selection_data(GNT_TREE(entry->ddown)));
+			destroy_suggest(entry);
+			if (entry->word)
+			{
+				char *s = get_beginning_of_word(entry);
+				char *iter = text;
+				while (*s == *iter)
+				{
+					s++;
+					iter++;
+				}
+				gnt_entry_key_pressed(widget, iter);
+			}
+			else
+			{
+				gnt_entry_set_text(entry, text);
+			}
+			g_free(text);
+			return TRUE;
+		}
+
 		if (!iscntrl(text[0]))
 		{
 			const char *str, *next;
@@ -143,6 +313,9 @@
 
 				while (g_utf8_pointer_to_offset(entry->scroll, entry->cursor) >= widget->priv.width)
 					entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
+
+				if (entry->ddown)
+					show_suggest_dropdown(entry);
 			}
 			entry_redraw(widget);
 			return TRUE;
@@ -161,6 +334,8 @@
 					entry->scroll = g_utf8_find_prev_char(entry->start, entry->scroll);
 
 				entry_redraw(widget);
+				if (entry->ddown)
+					show_suggest_dropdown(entry);
 				return TRUE;
 			}
 		}
@@ -174,6 +349,32 @@
 {
 	GntEntry *entry = GNT_ENTRY(widget);
 	g_free(entry->start);
+
+	if (entry->history)
+	{
+		entry->history = g_list_first(entry->history);
+		g_list_foreach(entry->history, (GFunc)g_free, NULL);
+		g_list_free(entry->history);
+	}
+
+	if (entry->suggests)
+	{
+		g_list_foreach(entry->suggests, (GFunc)g_free, NULL);
+		g_list_free(entry->suggests);
+	}
+
+	if (entry->ddown)
+	{
+		gnt_widget_destroy(entry->ddown->parent);
+	}
+}
+
+static void
+gnt_entry_lost_focus(GntWidget *widget)
+{
+	GntEntry *entry = GNT_ENTRY(widget);
+	destroy_suggest(entry);
+	entry_redraw(widget);
 }
 
 static void
@@ -185,6 +386,7 @@
 	parent_class->map = gnt_entry_map;
 	parent_class->size_request = gnt_entry_size_request;
 	parent_class->key_pressed = gnt_entry_key_pressed;
+	parent_class->lost_focus = gnt_entry_lost_focus;
 
 	DEBUG;
 }
@@ -197,6 +399,13 @@
 
 	entry->flag = GNT_ENTRY_FLAG_ALL;
 	entry->max = 0;
+	
+	entry->histlength = 0;
+	entry->history = NULL;
+
+	entry->word = TRUE;
+	entry->always = FALSE;
+	entry->suggests = NULL;
 
 	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry),
 			GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS);
@@ -302,6 +511,7 @@
 	gnt_entry_set_text(entry, NULL);
 	entry->scroll = entry->cursor = entry->end = entry->start;
 	entry_redraw(GNT_WIDGET(entry));
+	destroy_suggest(entry);
 }
 
 void gnt_entry_set_masked(GntEntry *entry, gboolean set)
@@ -309,3 +519,90 @@
 	entry->masked = set;
 }
 
+void gnt_entry_add_to_history(GntEntry *entry, const char *text)
+{
+	g_return_if_fail(entry->history != NULL);   /* Need to set_history_length first */
+
+	if (g_list_length(entry->history) >= entry->histlength)
+		return;
+
+	entry->history = g_list_first(entry->history);
+	g_free(entry->history->data);
+	entry->history->data = g_strdup(text);
+	entry->history = g_list_prepend(entry->history, NULL);
+}
+
+void gnt_entry_set_history_length(GntEntry *entry, int num)
+{
+	if (num == 0)
+	{
+		entry->histlength = num;
+		if (entry->history)
+		{
+			entry->history = g_list_first(entry->history);
+			g_list_foreach(entry->history, (GFunc)g_free, NULL);
+			g_list_free(entry->history);
+			entry->history = NULL;
+		}
+		return;
+	}
+
+	if (entry->histlength == 0)
+	{
+		entry->histlength = num;
+		entry->history = g_list_append(NULL, NULL);
+		return;
+	}
+
+	if (num > 0 && num < entry->histlength)
+	{
+		GList *first, *iter;
+		int index = 0;
+		for (first = entry->history, index = 0; first->prev; first = first->prev, index++);
+		while ((iter = g_list_nth(first, num)) != NULL)
+		{
+			g_free(iter->data);
+			first = g_list_delete_link(first, iter);
+		}
+		entry->histlength = num;
+		if (index >= num)
+			entry->history = g_list_last(first);
+		return;
+	}
+
+	entry->histlength = num;
+}
+
+void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word)
+{
+	entry->word = word;
+}
+
+void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always)
+{
+	entry->always = always;
+}
+
+void gnt_entry_add_suggest(GntEntry *entry, const char *text)
+{
+	GList *find;
+
+	if (!text || !*text)
+		return;
+	
+	find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate);
+	if (find)
+		return;
+	entry->suggests = g_list_append(entry->suggests, g_strdup(text));
+}
+
+void gnt_entry_remove_suggest(GntEntry *entry, const char *text)
+{
+	GList *find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate);
+	if (find)
+	{
+		g_free(find->data);
+		entry->suggests = g_list_delete_link(entry->suggests, find);
+	}
+}
+
--- a/console/libgnt/gntentry.h	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntentry.h	Sat Aug 12 10:27:29 2006 +0000
@@ -50,6 +50,14 @@
 	
 	int max;        /* 0 means infinite */
 	gboolean masked;
+
+	GList *history; /* History of the strings. User can use this by pressing ctrl+up/down */
+	int histlength; /* How long can the history be? */
+
+	GList *suggests;    /* List of suggestions */
+	gboolean word;      /* Are the suggestions for only a word, or for the whole thing? */
+	gboolean always;    /* Should the list of suggestions show at all times, or only on tab-press? */
+	GntWidget *ddown;   /* The dropdown with the suggested list */
 };
 
 struct _GnEntryClass
@@ -70,7 +78,6 @@
 
 void gnt_entry_set_max(GntEntry *entry, int max);
 
-/* XXX: For now, call gnt_entry_clear before calling this */
 void gnt_entry_set_text(GntEntry *entry, const char *text);
 
 void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag);
@@ -81,6 +88,18 @@
 
 void gnt_entry_set_masked(GntEntry *entry, gboolean set);
 
+void gnt_entry_add_to_history(GntEntry *entry, const char *text);
+
+void gnt_entry_set_history_length(GntEntry *entry, int num);
+
+void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word);
+
+void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always);
+
+void gnt_entry_add_suggest(GntEntry *entry, const char *text);
+
+void gnt_entry_remove_suggest(GntEntry *entry, const char *text);
+
 G_END_DECLS
 
 #endif /* GNT_ENTRY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/libgnt/gntkeys.c	Sat Aug 12 10:27:29 2006 +0000
@@ -0,0 +1,29 @@
+#include "gntkeys.h"
+
+#include <string.h>
+
+void gnt_keys_refine(char *text)
+{
+	if (text[0] == 27)
+	{
+		/* These are for urxvt */
+		if (strcmp(text + 1, "Oa") == 0)
+		{
+			strcpy(text + 1, GNT_KEY_CTRL_UP);
+		}
+		else if (strcmp(text + 1, "Ob") == 0)
+		{
+			strcpy(text + 1, GNT_KEY_CTRL_DOWN);
+		}
+	}
+	else if ((unsigned char)text[0] == 195)
+	{
+		/* These for xterm */
+		if (text[2] == 0)
+		{
+			text[0] = 27;
+			text[1] -= 64;
+		}
+	}
+}
+
--- a/console/libgnt/gntkeys.h	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntkeys.h	Sat Aug 12 10:27:29 2006 +0000
@@ -9,6 +9,11 @@
 #define GNT_KEY_UP     "[A"
 #define GNT_KEY_DOWN   "[B"
 
+#define GNT_KEY_CTRL_UP     "[1;5A"
+#define GNT_KEY_CTRL_DOWN   "[1;5B"
+#define GNT_KEY_CTRL_RIGHT  "[1;5C"
+#define GNT_KEY_CTRL_LEFT   "[1;5D"
+
 #define GNT_KEY_PGUP   "[5~"
 #define GNT_KEY_PGDOWN "[6~"
 
@@ -17,4 +22,9 @@
 #define GNT_KEY_BACKSPACE "\177"
 #define GNT_KEY_DEL    "[3~"
 
+/**
+ * This will do stuff with the terminal settings and stuff.
+ */
+void gnt_keys_refine(char *text);
+
 #endif
--- a/console/libgnt/gntmain.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntmain.c	Sat Aug 12 10:27:29 2006 +0000
@@ -13,6 +13,11 @@
 #include <unistd.h>
 #include <string.h>
 
+/**
+ * Notes: Interesting functions to look at:
+ * 	scr_dump, scr_init, scr_restore: for workspaces
+ */
+
 static int lock_focus_list;
 static GList *focus_list;
 
@@ -52,6 +57,14 @@
 static void draw_taskbar();
 static void bring_on_top(GntWidget *widget);
 
+static gboolean
+update_screen(gpointer null)
+{
+	update_panels();
+	doupdate();
+	return TRUE;
+}
+
 void gnt_screen_take_focus(GntWidget *widget)
 {
 	GntWidget *w = NULL;
@@ -119,8 +132,7 @@
 		GntNode *nd = g_hash_table_lookup(nodes, window_list.window);
 		top_panel(nd->panel);
 	}
-	update_panels();
-	doupdate();
+	update_screen(NULL);
 	draw_taskbar();
 }
 
@@ -437,6 +449,8 @@
 		dump_screen();
 	}
 
+	gnt_keys_refine(buffer);
+
 	if (mode == GNT_KP_MODE_NORMAL)
 	{
 		if (focus_list)
@@ -499,6 +513,14 @@
 				{
 					shift_window(focus_list->data, 1);
 				}
+				else if (strcmp(buffer + 1, "l") == 0)
+				{
+					touchwin(stdscr);
+					touchwin(newscr);
+					wrefresh(newscr);
+					update_screen(NULL);
+					draw_taskbar();
+				}
 			}
 		}
 	}
@@ -557,8 +579,7 @@
 				GntNode *node = g_hash_table_lookup(nodes, widget);
 				gnt_widget_set_position(widget, x, y);
 				move_panel(node->panel, y, x);
-				update_panels();
-				doupdate();
+				update_screen(NULL);
 			}
 		}
 		else if (*buffer == '\r')
@@ -656,6 +677,10 @@
 		ascii_only = TRUE;
 
 	initscr();
+	typeahead(-1);
+	noecho();
+	curs_set(0);
+
 	gnt_init_colors();
 	gnt_init_styles();
 
@@ -671,7 +696,6 @@
 	nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node);
 
 	wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
-	noecho();
 	refresh();
 #if 0
 	mousemask(NCURSES_BUTTON_PRESSED | NCURSES_BUTTON_RELEASED | REPORT_MOUSE_POSITION, NULL);
@@ -729,8 +753,7 @@
 		}
 	}
 
-	update_panels();
-	doupdate();
+	update_screen(NULL);
 }
 
 void gnt_screen_release(GntWidget *widget)
@@ -750,8 +773,7 @@
 		gnt_tree_remove(GNT_TREE(window_list.tree), widget);
 	}
 
-	update_panels();
-	doupdate();
+	update_screen(NULL);
 }
 
 void gnt_screen_update(GntWidget *widget)
@@ -779,8 +801,7 @@
 		top_panel(nd->panel);
 	}
 
-	update_panels();
-	doupdate();
+	update_screen(NULL);
 }
 
 gboolean gnt_widget_has_focus(GntWidget *widget)
@@ -844,8 +865,7 @@
 		gnt_widget_draw(widget);
 		replace_panel(node->panel, widget->window);
 		show_panel(node->panel);
-		update_panels();
-		doupdate();
+		update_screen(NULL);
 	}
 }
 
--- a/console/libgnt/gnttree.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gnttree.c	Sat Aug 12 10:27:29 2006 +0000
@@ -770,6 +770,15 @@
 	}
 }
 
+void gnt_tree_remove_all(GntTree *tree)
+{
+	tree->root = NULL;
+	g_hash_table_remove_all(tree->hash);
+	g_list_free(tree->list);
+	tree->list = NULL;
+	tree->current = tree->top = tree->bottom = NULL;
+}
+
 int gnt_tree_get_selection_visible_line(GntTree *tree)
 {
 	return get_distance(tree->top, tree->current) +
--- a/console/libgnt/gnttree.h	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gnttree.h	Sat Aug 12 10:27:29 2006 +0000
@@ -85,6 +85,8 @@
 
 void gnt_tree_remove(GntTree *tree, gpointer key);
 
+void gnt_tree_remove_all(GntTree *tree);
+
 /* Returns the visible line number of the selected row */
 int gnt_tree_get_selection_visible_line(GntTree *tree);
 
--- a/console/libgnt/gntwidget.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/gntwidget.c	Sat Aug 12 10:27:29 2006 +0000
@@ -80,6 +80,23 @@
 	return TRUE;
 }
 
+static gboolean
+gnt_boolean_handled_accumulator(GSignalInvocationHint *ihint,
+				  GValue                *return_accu,
+				  const GValue          *handler_return,
+				  gpointer               dummy)
+{
+	gboolean continue_emission;
+	gboolean signal_handled;
+
+	signal_handled = g_value_get_boolean (handler_return);
+	g_value_set_boolean (return_accu, signal_handled);
+	continue_emission = !signal_handled;
+
+	return continue_emission;
+}
+
+
 static void
 gnt_widget_class_init(GntWidgetClass *klass)
 {
@@ -194,7 +211,7 @@
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
 					 G_STRUCT_OFFSET(GntWidgetClass, key_pressed),
-					 NULL, NULL,
+					 gnt_boolean_handled_accumulator, NULL,
 					 gnt_closure_marshal_BOOLEAN__STRING,
 					 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
 	DEBUG;
--- a/console/libgnt/test/tv.c	Sat Aug 12 10:20:19 2006 +0000
+++ b/console/libgnt/test/tv.c	Sat Aug 12 10:27:29 2006 +0000
@@ -14,19 +14,24 @@
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view),
 				gnt_entry_get_text(GNT_ENTRY(w)),
 				GNT_TEXT_FLAG_HIGHLIGHT);
+		gnt_entry_add_to_history(GNT_ENTRY(w), gnt_entry_get_text(GNT_ENTRY(w)));
 		gnt_text_view_next_line(GNT_TEXT_VIEW(view));
 		gnt_entry_clear(GNT_ENTRY(w));
 		if (gnt_text_view_get_lines_below(GNT_TEXT_VIEW(view)) <= 1)
 			gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0);
+		gnt_entry_remove_suggest(GNT_ENTRY(w), "acb");
 
 		return TRUE;
 	}
 	else if (key[0] == 27)
 	{
-		if (strcmp(key+1, GNT_KEY_UP))
+		if (strcmp(key+1, GNT_KEY_UP) == 0)
+			gnt_text_view_scroll(GNT_TEXT_VIEW(view), -1);
+		else if (strcmp(key+1, GNT_KEY_DOWN) == 0)
 			gnt_text_view_scroll(GNT_TEXT_VIEW(view), 1);
-		else if (strcmp(key+1, GNT_KEY_DOWN))
-			gnt_text_view_scroll(GNT_TEXT_VIEW(view), -1);
+		else
+			return FALSE;
+		return TRUE;
 	}
 		
 	return FALSE;
@@ -53,6 +58,15 @@
 	gnt_widget_set_name(entry, "entry");
 	GNT_WIDGET_SET_FLAGS(entry, GNT_WIDGET_CAN_TAKE_FOCUS);
 
+	gnt_entry_set_word_suggest(GNT_ENTRY(entry), TRUE);
+	gnt_entry_set_always_suggest(GNT_ENTRY(entry), FALSE);
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "a");
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "ab");
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "abc");
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "abcd");
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "abcde");
+	gnt_entry_add_suggest(GNT_ENTRY(entry), "acb");
+
 	view = gnt_text_view_new();
 	gnt_widget_set_name(view, "view");
 
@@ -65,7 +79,8 @@
 
 	gnt_widget_show(hbox);
 
-	g_signal_connect(G_OBJECT(entry), "key_pressed", G_CALLBACK(key_pressed), view);
+	gnt_entry_set_history_length(GNT_ENTRY(entry), -1);
+	g_signal_connect_after(G_OBJECT(entry), "key_pressed", G_CALLBACK(key_pressed), view);
 
 #ifdef STANDALONE
 	gnt_main();