finch/libgnt/gntentry.c

changeset 39360
e7bed293aad5
parent 39302
64aabebb476b
child 39361
a1068caa3600
--- a/finch/libgnt/gntentry.c	Thu Nov 15 14:32:09 2018 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1269 +0,0 @@
-/*
- * GNT - The GLib Ncurses Toolkit
- *
- * GNT is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include <ctype.h>
-#include <string.h>
-
-#include "gntinternal.h"
-#include "gntbox.h"
-#include "gntentry.h"
-#include "gntstyle.h"
-#include "gnttree.h"
-#include "gntutils.h"
-
-enum
-{
-	SIG_TEXT_CHANGED,
-	SIG_COMPLETION,
-	SIGS,
-};
-
-typedef enum
-{
-	ENTRY_JAIL = -1,    /* Suspend the kill ring. */
-	ENTRY_DEL_BWD_WORD = 1,
-	ENTRY_DEL_BWD_CHAR,
-	ENTRY_DEL_FWD_WORD,
-	ENTRY_DEL_FWD_CHAR,
-	ENTRY_DEL_EOL,
-	ENTRY_DEL_BOL,
-} GntEntryAction;
-
-struct _GntEntryKillRing
-{
-	GString *buffer;
-	GntEntryAction last;
-};
-
-struct _GntEntrySearch
-{
-	char *needle;
-};
-
-static guint signals[SIGS] = { 0 };
-
-static GntWidgetClass *parent_class = NULL;
-
-static gboolean gnt_entry_key_pressed(GntWidget *widget, const char *text);
-static void gnt_entry_set_text_internal(GntEntry *entry, const char *text);
-
-static gboolean
-update_kill_ring(GntEntry *entry, GntEntryAction action, const char *text, int len)
-{
-	if (action < 0) {
-		entry->killring->last = action;
-		return FALSE;
-	}
-
-	if (len == 0)
-		len = strlen(text);
-	else if (len < 0) {
-		text += len;
-		len = -len;
-	}
-
-	if (action != entry->killring->last) {
-		struct {
-			GntEntryAction one;
-			GntEntryAction two;
-		} merges[] = {
-			{ENTRY_DEL_BWD_WORD, ENTRY_DEL_FWD_WORD},
-			{ENTRY_DEL_BWD_CHAR, ENTRY_DEL_FWD_CHAR},
-			{ENTRY_DEL_BOL, ENTRY_DEL_EOL},
-			{ENTRY_JAIL, ENTRY_JAIL},
-		};
-		int i;
-
-		for (i = 0; merges[i].one != ENTRY_JAIL; i++) {
-			if (merges[i].one == entry->killring->last &&
-					merges[i].two == action) {
-				g_string_append_len(entry->killring->buffer, text, len);
-				break;
-			} else if (merges[i].one == action &&
-					merges[i].two == entry->killring->last) {
-				g_string_prepend_len(entry->killring->buffer, text, len);
-				break;
-			}
-		}
-		if (merges[i].one == ENTRY_JAIL) {
-			g_string_assign(entry->killring->buffer, text);
-			g_string_truncate(entry->killring->buffer, len);
-		}
-		entry->killring->last = action;
-	} else {
-		if (action == ENTRY_DEL_BWD_CHAR || action == ENTRY_DEL_BWD_WORD)
-			g_string_prepend_len(entry->killring->buffer, text, len);
-		else
-			g_string_append_len(entry->killring->buffer, text, len);
-	}
-	return TRUE;
-}
-
-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 (isspace(*t))
-			break;
-		s = t;
-	}
-	return s;
-}
-
-static gboolean
-complete_suggest(GntEntry *entry, const char *text)
-{
-	int offstart = 0, offend = 0;
-
-	if (entry->word) {
-		char *s = get_beginning_of_word(entry);
-		const char *iter = text;
-		offstart = g_utf8_pointer_to_offset(entry->start, s);
-		while (*iter && toupper(*s) == toupper(*iter)) {
-			*s++ = *iter++;
-		}
-		if (*iter) {
-			gnt_entry_key_pressed(GNT_WIDGET(entry), iter);
-		}
-		offend = g_utf8_pointer_to_offset(entry->start, entry->cursor);
-	} else {
-		offstart = 0;
-		gnt_entry_set_text_internal(entry, text);
-		offend = g_utf8_strlen(text, -1);
-	}
-
-	g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0,
-			entry->start + offstart, entry->start + offend);
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	return TRUE;
-}
-
-static int
-max_common_prefix(const char *s, const char *t)
-{
-	const char *f = s;
-	while (*f && *t && *f == *t++)
-		f++;
-	return f - s;
-}
-
-static gboolean
-show_suggest_dropdown(GntEntry *entry)
-{
-	char *suggest = NULL;
-	gsize len;
-	int offset = 0, x, y;
-	int count = 0;
-	GList *iter;
-	const char *text = NULL;
-	const char *sgst = NULL;
-	int max = -1;
-
-	if (entry->word)
-	{
-		char *s = get_beginning_of_word(entry);
-		suggest = g_strndup(s, entry->cursor - s);
-		if (entry->scroll < s)
-			offset = gnt_util_onscreen_width(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_tree_set_compare_func(GNT_TREE(entry->ddown), (GCompareFunc)g_utf8_collate);
-		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);
-	}
-	else
-		gnt_tree_remove_all(GNT_TREE(entry->ddown));
-
-	for (count = 0, iter = entry->suggests; iter; iter = iter->next)
-	{
-		text = iter->data;
-		if (g_ascii_strncasecmp(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++;
-			if (max == -1)
-				max = strlen(text) - len;
-			else if (max)
-				max = MIN(max, max_common_prefix(sgst + len, text + len));
-			sgst = text;
-		}
-	}
-	g_free(suggest);
-
-	if (count == 0) {
-		destroy_suggest(entry);
-		return FALSE;
-	} else if (count == 1) {
-		char *store = g_strndup(entry->start, entry->end - entry->start);
-		gboolean ret;
-
-		destroy_suggest(entry);
-		complete_suggest(entry, sgst);
-
-		ret = (strncmp(store, entry->start, entry->end - entry->start) != 0);
-		g_free(store);
-		return ret;
-	} else {
-		if (max > 0) {
-			GntWidget *ddown = entry->ddown;
-			char *match = g_strndup(sgst + len, max);
-			entry->ddown = NULL;
-			gnt_entry_key_pressed(GNT_WIDGET(entry), match);
-			g_free(match);
-			if (entry->ddown)
-				gnt_widget_destroy(ddown);
-			else
-				entry->ddown = ddown;
-		}
-		gnt_widget_draw(entry->ddown->parent);
-	}
-
-	return TRUE;
-}
-
-static void
-gnt_entry_draw(GntWidget *widget)
-{
-	GntEntry *entry = GNT_ENTRY(widget);
-	int stop;
-	gboolean focus;
-	int curpos;
-
-	if ((focus = gnt_widget_has_focus(widget)))
-		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TEXT_NORMAL));
-	else
-		wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
-
-	if (entry->masked)
-	{
-		mvwhline(widget->window, 0, 0, gnt_ascii_only() ? '*' : ACS_BULLET,
-				g_utf8_pointer_to_offset(entry->scroll, entry->end));
-	}
-	else
-		mvwprintw(widget->window, 0, 0, "%s", C_(entry->scroll));
-
-	stop = gnt_util_onscreen_width(entry->scroll, entry->end);
-	if (stop < widget->priv.width)
-		mvwhline(widget->window, 0, stop, GNT_ENTRY_CHAR, widget->priv.width - stop);
-
-	curpos = gnt_util_onscreen_width(entry->scroll, entry->cursor);
-	if (focus)
-		mvwchgat(widget->window, 0, curpos, 1, A_REVERSE, GNT_COLOR_TEXT_NORMAL, NULL);
-	(void)wmove(widget->window, 0, curpos);
-
-	GNTDEBUG;
-}
-
-static void
-gnt_entry_size_request(GntWidget *widget)
-{
-	if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED))
-	{
-		widget->priv.height = 1;
-		widget->priv.width = 20;
-	}
-}
-
-static void
-gnt_entry_map(GntWidget *widget)
-{
-	if (widget->priv.width == 0 || widget->priv.height == 0)
-		gnt_widget_size_request(widget);
-	GNTDEBUG;
-}
-
-static void
-entry_redraw(GntWidget *widget)
-{
-	gnt_entry_draw(widget);
-	gnt_widget_queue_update(widget);
-}
-
-static void
-entry_text_changed(GntEntry *entry)
-{
-	g_signal_emit(entry, signals[SIG_TEXT_CHANGED], 0);
-}
-
-static gboolean
-move_back(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->cursor <= entry->start)
-		return FALSE;
-	entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor);
-	if (entry->cursor < entry->scroll)
-		entry->scroll = entry->cursor;
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	entry_redraw(GNT_WIDGET(entry));
-	return TRUE;
-}
-
-static gboolean
-move_forward(GntBindable *bind, GList *list)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->cursor >= entry->end)
-		return FALSE;
-	entry->cursor = g_utf8_find_next_char(entry->cursor, NULL);
-	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
-		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	entry_redraw(GNT_WIDGET(entry));
-	return TRUE;
-}
-
-static gboolean
-backspace(GntBindable *bind, GList *null)
-{
-	int len;
-	GntEntry *entry = GNT_ENTRY(bind);
-
-	if (entry->cursor <= entry->start)
-		return TRUE;
-
-	len = entry->cursor - g_utf8_find_prev_char(entry->start, entry->cursor);
-	update_kill_ring(entry, ENTRY_JAIL, entry->cursor, -len);
-	entry->cursor -= len;
-
-	memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor);
-	entry->end -= len;
-
-	if (entry->scroll > entry->start)
-		entry->scroll = g_utf8_find_prev_char(entry->start, entry->scroll);
-
-	entry_redraw(GNT_WIDGET(entry));
-	if (entry->ddown)
-		show_suggest_dropdown(entry);
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-static gboolean
-delkey(GntBindable *bind, GList *null)
-{
-	int len;
-	GntEntry *entry = GNT_ENTRY(bind);
-
-	if (entry->cursor >= entry->end)
-		return FALSE;
-
-	len = g_utf8_find_next_char(entry->cursor, NULL) - entry->cursor;
-	update_kill_ring(entry, ENTRY_JAIL, entry->cursor, len);
-	memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor - len + 1);
-	entry->end -= len;
-	entry_redraw(GNT_WIDGET(entry));
-
-	if (entry->ddown)
-		show_suggest_dropdown(entry);
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-static gboolean
-move_start(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	entry->scroll = entry->cursor = entry->start;
-	entry_redraw(GNT_WIDGET(entry));
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	return TRUE;
-}
-
-static gboolean
-move_end(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	entry->cursor = entry->end;
-	/* This should be better than this */
-	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width)
-		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
-	entry_redraw(GNT_WIDGET(entry));
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	return TRUE;
-}
-
-static gboolean
-history_next(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->histlength && entry->history->prev)
-	{
-		entry->history = entry->history->prev;
-		gnt_entry_set_text_internal(entry, entry->history->data);
-		destroy_suggest(entry);
-		entry_text_changed(entry);
-
-		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-history_prev(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->histlength && 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_internal(entry, entry->history->data);
-		destroy_suggest(entry);
-		entry_text_changed(entry);
-
-		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-history_search(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	GList *iter;
-	const char *current;
-
-	if (entry->history->prev && entry->search->needle)
-		current = entry->search->needle;
-	else
-		current = gnt_entry_get_text(entry);
-
-	if (!entry->histlength || !entry->history->next || !*current)
-		return FALSE;
-
-	for (iter = entry->history->next; iter; iter = iter->next) {
-		const char *str = iter->data;
-		/* A more utf8-friendly version of strstr would have been better, but
-		 * for now, this will have to do. */
-		if (strstr(str, current) != NULL)
-			break;
-	}
-
-	if (!iter)
-		return TRUE;
-
-	if (entry->history->prev == NULL) {
-		/* We are doing it for the first time. Save the current contents */
-		char *text = g_strdup(gnt_entry_get_text(entry));
-
-		g_free(entry->search->needle);
-		entry->search->needle = g_strdup(current);
-
-		g_free(entry->history->data);
-		entry->history->data = text;
-	}
-
-	entry->history = iter;
-	gnt_entry_set_text_internal(entry, entry->history->data);
-	destroy_suggest(entry);
-	entry_text_changed(entry);
-
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	return TRUE;
-}
-
-static gboolean
-clipboard_paste(GntBindable *bind, GList *n)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	gchar *i, *text, *a, *all;
-	text = i = gnt_get_clipboard_string();
-	while (*i != '\0') {
-		i = g_utf8_next_char(i);
-		if (*i == '\r' || *i == '\n')
-			*i = ' ';
-	}
-	a = g_strndup(entry->start, entry->cursor - entry->start);
-	all = g_strconcat(a, text, entry->cursor, NULL);
-	gnt_entry_set_text_internal(entry, all);
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	g_free(a);
-	g_free(text);
-	g_free(all);
-	return TRUE;
-}
-
-static gboolean
-suggest_show(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down", NULL);
-		return TRUE;
-	}
-	return show_suggest_dropdown(entry);
-}
-
-static gboolean
-suggest_next(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down", NULL);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-suggest_prev(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-up", NULL);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-suggest_next_page(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "page-down", NULL);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-suggest_prev_page(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "page-up", NULL);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-del_to_home(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->cursor <= entry->start)
-		return TRUE;
-	update_kill_ring(entry, ENTRY_DEL_BOL, entry->start, entry->cursor - entry->start);
-	memmove(entry->start, entry->cursor, entry->end - entry->cursor);
-	entry->end -= (entry->cursor - entry->start);
-	entry->cursor = entry->scroll = entry->start;
-	memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
-	entry_redraw(GNT_WIDGET(bind));
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-static gboolean
-del_to_end(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	if (entry->end <= entry->cursor)
-		return TRUE;
-	update_kill_ring(entry, ENTRY_DEL_EOL, entry->cursor, entry->end - entry->cursor);
-	entry->end = entry->cursor;
-	memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
-	entry_redraw(GNT_WIDGET(bind));
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-#define SAME(a,b)    ((g_unichar_isalnum(a) && g_unichar_isalnum(b)) || \
-				(g_unichar_isspace(a) && g_unichar_isspace(b)) || \
-				(g_unichar_iswide(a) && g_unichar_iswide(b)) || \
-				(g_unichar_ispunct(a) && g_unichar_ispunct(b)))
-
-static const char *
-begin_word(const char *text, const char *begin)
-{
-	gunichar ch = 0;
-	while (text > begin && (!*text || g_unichar_isspace(g_utf8_get_char(text))))
-		text = g_utf8_find_prev_char(begin, text);
-	ch = g_utf8_get_char(text);
-	while ((text = g_utf8_find_prev_char(begin, text)) >= begin) {
-		gunichar cur = g_utf8_get_char(text);
-		if (!SAME(ch, cur))
-			break;
-	}
-
-	return (text ? g_utf8_find_next_char(text, NULL) : begin);
-}
-
-static const char *
-next_begin_word(const char *text, const char *end)
-{
-	gunichar ch = 0;
-
-	while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
-		text = g_utf8_find_next_char(text, end);
-
-	if (text) {
-		ch = g_utf8_get_char(text);
-		while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
-			gunichar cur = g_utf8_get_char(text);
-			if (!SAME(ch, cur))
-				break;
-		}
-	}
-	return (text ? text : end);
-}
-
-#undef SAME
-static gboolean
-move_back_word(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	const char *iter = g_utf8_find_prev_char(entry->start, entry->cursor);
-
-	if (iter < entry->start)
-		return TRUE;
-	iter = begin_word(iter, entry->start);
-	entry->cursor = (char*)iter;
-	if (entry->cursor < entry->scroll)
-		entry->scroll = entry->cursor;
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	entry_redraw(GNT_WIDGET(bind));
-	return TRUE;
-}
-
-static gboolean
-del_prev_word(GntBindable *bind, GList *null)
-{
-	GntWidget *widget = GNT_WIDGET(bind);
-	GntEntry *entry = GNT_ENTRY(bind);
-	char *iter = g_utf8_find_prev_char(entry->start, entry->cursor);
-	int count;
-
-	if (iter < entry->start)
-		return TRUE;
-	iter = (char*)begin_word(iter, entry->start);
-	count = entry->cursor - iter;
-	update_kill_ring(entry, ENTRY_DEL_BWD_WORD, iter, count);
-	memmove(iter, entry->cursor, entry->end - entry->cursor);
-	entry->end -= count;
-	entry->cursor = iter;
-	if (entry->cursor <= entry->scroll) {
-		entry->scroll = entry->cursor - widget->priv.width + 2;
-		if (entry->scroll < entry->start)
-			entry->scroll = entry->start;
-	}
-	memset(entry->end, '\0', entry->buffer - (entry->end - entry->start));
-	entry_redraw(widget);
-	entry_text_changed(entry);
-
-	return TRUE;
-}
-
-static gboolean
-move_forward_word(GntBindable *bind, GList *list)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	GntWidget *widget = GNT_WIDGET(bind);
-	entry->cursor = (char *)next_begin_word(entry->cursor, entry->end);
-	while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width) {
-		entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
-	}
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	entry_redraw(widget);
-	return TRUE;
-}
-
-static gboolean
-delete_forward_word(GntBindable *bind, GList *list)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	GntWidget *widget = GNT_WIDGET(bind);
-	char *iter = (char *)next_begin_word(entry->cursor, entry->end);
-	int len = entry->end - iter + 1;
-	if (len <= 0)
-		return TRUE;
-	update_kill_ring(entry, ENTRY_DEL_FWD_WORD, entry->cursor, iter - entry->cursor);
-	memmove(entry->cursor, iter, len);
-	len = iter - entry->cursor;
-	entry->end -= len;
-	memset(entry->end, '\0', len);
-	entry_redraw(widget);
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-static gboolean
-transpose_chars(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	char *current, *prev;
-	char hold[8];  /* that's right */
-
-	if (entry->cursor <= entry->start)
-		return FALSE;
-
-	if (!*entry->cursor)
-		entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor);
-
-	current = entry->cursor;
-	prev = g_utf8_find_prev_char(entry->start, entry->cursor);
-	move_forward(bind, null);
-
-	/* Let's do this dance! */
-	memcpy(hold, prev, current - prev);
-	memmove(prev, current, entry->cursor - current);
-	memcpy(prev + (entry->cursor - current), hold, current - prev);
-
-	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	entry_redraw(GNT_WIDGET(entry));
-	entry_text_changed(entry);
-	return TRUE;
-}
-
-static gboolean
-entry_yank(GntBindable *bind, GList *null)
-{
-	GntEntry *entry = GNT_ENTRY(bind);
-	gnt_entry_key_pressed(GNT_WIDGET(entry), entry->killring->buffer->str);
-	return TRUE;
-}
-
-static gboolean
-gnt_entry_key_pressed(GntWidget *widget, const char *text)
-{
-	GntEntry *entry = GNT_ENTRY(widget);
-
-	if (text[0] == 27)
-	{
-		if (text[1] == 0)
-		{
-			destroy_suggest(entry);
-			return TRUE;
-		}
-
-		return FALSE;
-	}
-
-	if ((text[0] == '\r' || text[0] == ' ' || text[0] == '\n') && entry->ddown)
-	{
-		char *text = g_strdup(gnt_tree_get_selection_data(GNT_TREE(entry->ddown)));
-		destroy_suggest(entry);
-		complete_suggest(entry, text);
-		g_free(text);
-		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-		entry_text_changed(entry);
-		return TRUE;
-	}
-
-	if (!iscntrl(text[0]))
-	{
-		const char *str, *next;
-
-		for (str = text; *str; str = next)
-		{
-			gsize len;
-			next = g_utf8_find_next_char(str, NULL);
-			len = next - str;
-
-			/* Valid input? */
-			/* XXX: Is it necessary to use _unichar_ variants here? */
-			if (ispunct(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_PUNCT))
-				continue;
-			if (isspace(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_SPACE))
-				continue;
-			if (isalpha(*str) && !(entry->flag & GNT_ENTRY_FLAG_ALPHA))
-				continue;
-			if (isdigit(*str) && !(entry->flag & GNT_ENTRY_FLAG_INT))
-				continue;
-
-			/* Reached the max? */
-			if (entry->max && g_utf8_pointer_to_offset(entry->start, entry->end) >= entry->max)
-				continue;
-
-			if ((gsize)(entry->end + len - entry->start) >= entry->buffer)
-			{
-				/* This will cause the buffer to grow */
-				char *tmp = g_strdup(entry->start);
-				gnt_entry_set_text_internal(entry, tmp);
-				g_free(tmp);
-			}
-
-			memmove(entry->cursor + len, entry->cursor, entry->end - entry->cursor + 1);
-			entry->end += len;
-
-			while (str < next)
-			{
-				if (*str == '\r' || *str == '\n')
-					*entry->cursor = ' ';
-				else
-					*entry->cursor = *str;
-				entry->cursor++;
-				str++;
-			}
-
-			while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width)
-				entry->scroll = g_utf8_find_next_char(entry->scroll, NULL);
-
-			if (entry->ddown)
-				show_suggest_dropdown(entry);
-		}
-		update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-		entry_redraw(widget);
-		entry_text_changed(entry);
-		return TRUE;
-	}
-
-	if (text[0] == '\r' || text[0] == '\n') {
-		gnt_widget_activate(widget);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static void
-jail_killring(GntEntryKillRing *kr)
-{
-	g_string_free(kr->buffer, TRUE);
-	g_free(kr);
-}
-
-static void
-gnt_entry_destroy(GntWidget *widget)
-{
-	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);
-	}
-
-	g_free(entry->search->needle);
-	g_free(entry->search);
-
-	jail_killring(entry->killring);
-}
-
-static void
-gnt_entry_lost_focus(GntWidget *widget)
-{
-	GntEntry *entry = GNT_ENTRY(widget);
-	destroy_suggest(entry);
-	entry_redraw(widget);
-}
-
-static gboolean
-gnt_entry_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
-{
-	if (event == GNT_MIDDLE_MOUSE_DOWN) {
-		clipboard_paste(GNT_BINDABLE(widget), NULL);
-		return TRUE;
-	}
-	return FALSE;
-
-}
-
-static void
-gnt_entry_class_init(GntEntryClass *klass)
-{
-	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
-	char s[3] = {'\033', erasechar(), 0};
-
-	parent_class = GNT_WIDGET_CLASS(klass);
-	parent_class->clicked = gnt_entry_clicked;
-	parent_class->destroy = gnt_entry_destroy;
-	parent_class->draw = gnt_entry_draw;
-	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;
-
-	signals[SIG_TEXT_CHANGED] =
-		g_signal_new("text_changed",
-					 G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST,
-					 G_STRUCT_OFFSET(GntEntryClass, text_changed),
-					 NULL, NULL, NULL,
-					 G_TYPE_NONE, 0);
-
-	signals[SIG_COMPLETION] =
-		g_signal_new("completion",
-					 G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST,
-					 0, NULL, NULL, NULL,
-					 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
-
-	gnt_bindable_class_register_action(bindable, "cursor-home", move_start,
-				GNT_KEY_CTRL_A, NULL);
-	gnt_bindable_register_binding(bindable, "cursor-home", GNT_KEY_HOME, NULL);
-	gnt_bindable_class_register_action(bindable, "cursor-end", move_end,
-				GNT_KEY_CTRL_E, NULL);
-	gnt_bindable_register_binding(bindable, "cursor-end", GNT_KEY_END, NULL);
-	gnt_bindable_class_register_action(bindable, "delete-prev", backspace,
-				GNT_KEY_BACKSPACE, NULL);
-	gnt_bindable_register_binding(bindable, "delete-prev", s + 1, NULL);
-	gnt_bindable_register_binding(bindable, "delete-prev", GNT_KEY_CTRL_H, NULL);
-	gnt_bindable_class_register_action(bindable, "delete-next", delkey,
-				GNT_KEY_DEL, NULL);
-	gnt_bindable_register_binding(bindable, "delete-next", GNT_KEY_CTRL_D, NULL);
-	gnt_bindable_class_register_action(bindable, "delete-start", del_to_home,
-				GNT_KEY_CTRL_U, NULL);
-	gnt_bindable_class_register_action(bindable, "delete-end", del_to_end,
-				GNT_KEY_CTRL_K, NULL);
-	gnt_bindable_class_register_action(bindable, "delete-prev-word", del_prev_word,
-				GNT_KEY_CTRL_W, NULL);
-	gnt_bindable_register_binding(bindable, "delete-prev-word", s, NULL);
-	gnt_bindable_class_register_action(bindable, "cursor-prev-word", move_back_word,
-				"\033" "b", NULL);
-	gnt_bindable_class_register_action(bindable, "cursor-prev", move_back,
-				GNT_KEY_LEFT, NULL);
-	gnt_bindable_register_binding(bindable, "cursor-prev", GNT_KEY_CTRL_B, NULL);
-	gnt_bindable_class_register_action(bindable, "cursor-next", move_forward,
-				GNT_KEY_RIGHT, NULL);
-	gnt_bindable_register_binding(bindable, "cursor-next", GNT_KEY_CTRL_F, NULL);
-	gnt_bindable_class_register_action(bindable, "cursor-next-word", move_forward_word,
-				"\033" "f", NULL);
-	gnt_bindable_class_register_action(bindable, "delete-next-word", delete_forward_word,
-				"\033" "d", NULL);
-	gnt_bindable_class_register_action(bindable, "transpose-chars", transpose_chars,
-				GNT_KEY_CTRL_T, NULL);
-	gnt_bindable_class_register_action(bindable, "yank", entry_yank,
-				GNT_KEY_CTRL_Y, NULL);
-	gnt_bindable_class_register_action(bindable, "suggest-show", suggest_show,
-				"\t", NULL);
-	gnt_bindable_class_register_action(bindable, "suggest-next", suggest_next,
-				GNT_KEY_DOWN, NULL);
-	gnt_bindable_class_register_action(bindable, "suggest-prev", suggest_prev,
-				GNT_KEY_UP, NULL);
-	gnt_bindable_class_register_action(bindable, "suggest-next-page", suggest_next_page,
-				GNT_KEY_PGDOWN, NULL);
-	gnt_bindable_class_register_action(bindable, "suggest-prev-page", suggest_prev_page,
-				GNT_KEY_PGUP, NULL);
-	gnt_bindable_class_register_action(bindable, "history-next", history_next,
-				GNT_KEY_CTRL_DOWN, NULL);
-	gnt_bindable_class_register_action(bindable, "history-prev", history_prev,
-				GNT_KEY_CTRL_UP, NULL);
-	gnt_bindable_register_binding(bindable, "history-prev", GNT_KEY_CTRL_P, NULL);
-	gnt_bindable_register_binding(bindable, "history-next", GNT_KEY_CTRL_N, NULL);
-	gnt_bindable_class_register_action(bindable, "history-search", history_search,
-				GNT_KEY_CTRL_R, NULL);
-	gnt_bindable_class_register_action(bindable, "clipboard-paste", clipboard_paste,
-				GNT_KEY_CTRL_V, NULL);
-
-	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
-	GNTDEBUG;
-}
-
-static GntEntryKillRing *
-new_killring(void)
-{
-	GntEntryKillRing *kr = g_new0(GntEntryKillRing, 1);
-	kr->buffer = g_string_new(NULL);
-	return kr;
-}
-
-static void
-gnt_entry_init(GTypeInstance *instance, gpointer class)
-{
-	GntWidget *widget = GNT_WIDGET(instance);
-	GntEntry *entry = GNT_ENTRY(instance);
-
-	entry->flag = GNT_ENTRY_FLAG_ALL;
-	entry->max = 0;
-
-	entry->histlength = 0;
-	entry->history = NULL;
-
-	entry->word = TRUE;
-	entry->always = FALSE;
-	entry->suggests = NULL;
-	entry->killring = new_killring();
-	entry->search = g_new0(GntEntrySearch, 1);
-
-	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry),
-			GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS);
-	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry), GNT_WIDGET_GROW_X);
-
-	widget->priv.minw = 3;
-	widget->priv.minh = 1;
-
-	GNTDEBUG;
-}
-
-/******************************************************************************
- * GntEntry API
- *****************************************************************************/
-GType
-gnt_entry_get_type(void)
-{
-	static GType type = 0;
-
-	if(type == 0)
-	{
-		static const GTypeInfo info = {
-			sizeof(GntEntryClass),
-			NULL,					/* base_init		*/
-			NULL,					/* base_finalize	*/
-			(GClassInitFunc)gnt_entry_class_init,
-			NULL,					/* class_finalize	*/
-			NULL,					/* class_data		*/
-			sizeof(GntEntry),
-			0,						/* n_preallocs		*/
-			gnt_entry_init,			/* instance_init	*/
-			NULL					/* value_table		*/
-		};
-
-		type = g_type_register_static(GNT_TYPE_WIDGET,
-									  "GntEntry",
-									  &info, 0);
-	}
-
-	return type;
-}
-
-GntWidget *gnt_entry_new(const char *text)
-{
-	GntWidget *widget = g_object_new(GNT_TYPE_ENTRY, NULL);
-	GntEntry *entry = GNT_ENTRY(widget);
-
-	gnt_entry_set_text_internal(entry, text);
-
-	return widget;
-}
-
-static void
-gnt_entry_set_text_internal(GntEntry *entry, const char *text)
-{
-	int len;
-	int scroll, cursor;
-
-	g_free(entry->start);
-
-	if (text && text[0])
-	{
-		len = strlen(text);
-	}
-	else
-	{
-		len = 0;
-	}
-
-	entry->buffer = len + 128;
-
-	scroll = entry->scroll - entry->start;
-	cursor = entry->end - entry->cursor;
-
-	entry->start = g_new0(char, entry->buffer);
-	if (text)
-		snprintf(entry->start, len + 1, "%s", text);
-	entry->end = entry->start + len;
-
-	if ((entry->scroll = entry->start + scroll) > entry->end)
-		entry->scroll = entry->end;
-
-	if ((entry->cursor = entry->end - cursor) > entry->end)
-		entry->cursor = entry->end;
-
-	if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(entry), GNT_WIDGET_MAPPED))
-		entry_redraw(GNT_WIDGET(entry));
-}
-
-void gnt_entry_set_text(GntEntry *entry, const char *text)
-{
-	gboolean changed = TRUE;
-	if (text == NULL && entry->start == NULL)
-		changed = FALSE;
-	if (text && entry->start && g_utf8_collate(text, entry->start) == 0)
-		changed = FALSE;
-	gnt_entry_set_text_internal(entry, text);
-	if (changed)
-		entry_text_changed(entry);
-}
-
-void gnt_entry_set_max(GntEntry *entry, int max)
-{
-	entry->max = max;
-}
-
-void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag)
-{
-	entry->flag = flag;
-	/* XXX: Check the existing string to make sure the flags are respected? */
-}
-
-const char *gnt_entry_get_text(GntEntry *entry)
-{
-	return entry->start;
-}
-
-void gnt_entry_clear(GntEntry *entry)
-{
-	gnt_entry_set_text_internal(entry, NULL);
-	entry->scroll = entry->cursor = entry->end = entry->start;
-	entry_redraw(GNT_WIDGET(entry));
-	destroy_suggest(entry);
-	entry_text_changed(entry);
-}
-
-void gnt_entry_set_masked(GntEntry *entry, gboolean set)
-{
-	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 (entry->histlength >= 0 &&
-		g_list_length(entry->history) >= (gsize)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);
-	}
-}
-

mercurial