--- 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); - } -} -