diff --git a/ChangeLog b/ChangeLog index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 95514a69ad..50dfcb49f3 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:03:03 +0200 Paolo Molaro + + * gtk/gtkcombo.c: implemented completion in the entry. + 1998-05-18 * gtk/gtk{debug.h,signal.c,main.c}: Allow GTK_DEBUG=signals diff --git a/glib/ChangeLog b/glib/ChangeLog index 541430f3c3..adf2e3393e 100644 --- a/glib/ChangeLog +++ b/glib/ChangeLog @@ -1,3 +1,7 @@ +Tue, 19 May 1998 09:00:02 +0200 §Paolo Molaro + + * gcompletion.c: generic functions for completion... + Sun May 17 10:48:27 1998 Tim Janik * gscanner.c (g_scanner_unexp_token): provide usefull default diff --git a/glib/Makefile.am b/glib/Makefile.am index ef28c3e2f4..b2c617d7af 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -7,6 +7,7 @@ lib_LTLIBRARIES = libglib-1.1.la libglib_1_1_la_SOURCES = \ garray.c \ gcache.c \ + gcompletion.c \ gdataset.c \ gerror.c \ ghash.c \ diff --git a/glib/gcompletion.c b/glib/gcompletion.c new file mode 100644 index 0000000000..947a6bd60a --- /dev/null +++ b/glib/gcompletion.c @@ -0,0 +1,238 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "glib.h" +#include + +static void completion_check_cache (GCompletion* cmp, gchar** new_prefix); + +GCompletion* +g_completion_new (GCompletionFunc func) { + GCompletion* gcomp; + + gcomp = g_new(GCompletion, 1); + gcomp->items = NULL; + gcomp->cache = NULL; + gcomp->prefix = NULL; + if ( func ) + gcomp->func = func; + else + gcomp->func = NULL; + return gcomp; +} + +void +g_completion_add_items (GCompletion* cmp, GList* items) { + GList* it; + + g_return_if_fail( cmp != NULL); + g_return_if_fail( items != NULL); + + /* optimize adding to cache? */ + if ( cmp->cache ) { + g_list_free(cmp->cache); + cmp->cache = NULL; + } + if ( cmp->prefix ) { + g_free(cmp->prefix); + cmp->prefix = NULL; + } + + it = items; + while ( it ) { + cmp->items = g_list_prepend(cmp->items, it->data); + it = it->next; + } +} + +void +g_completion_remove_items (GCompletion* cmp, GList* items) { + GList* it; + + g_return_if_fail( cmp != NULL); + g_return_if_fail( items != NULL); + + it = items; + while ( cmp->items && it ) { + cmp->items = g_list_remove(cmp->items, it->data); + it = it->next; + } + it = items; + while ( cmp->cache && it ) { + cmp->cache = g_list_remove(cmp->cache, it->data); + it = it->next; + } +} + +void +g_completion_clear_items (GCompletion* cmp) { + g_return_if_fail(cmp != NULL); + + g_list_free(cmp->items); + cmp->items = NULL; + g_list_free(cmp->cache); + cmp->cache = NULL; + g_free(cmp->prefix); + cmp->prefix = NULL; +} + +static void +completion_check_cache (GCompletion* cmp, gchar** new_prefix) { + register GList* list; + register gint len; + register gint i; + register gint plen; + gchar* postfix=NULL; + gchar* s; + + if ( !new_prefix ) + return; + if ( !cmp->cache ) { + *new_prefix = NULL; + return; + } + + len = strlen(cmp->prefix); + list = cmp->cache; + s = cmp->func?(*cmp->func)(list->data):(gchar*)list->data; + postfix = s + len; + plen = strlen(postfix); + list = list->next; + + while (list && plen) { + s = cmp->func?(*cmp->func)(list->data):(gchar*)list->data; + s += len; + for (i=0; i < plen; ++i) { + if ( postfix[i] != s[i] ) + break; + } + plen = i; + list = list->next; + } + + *new_prefix = g_new0(gchar, len+plen+1); + strncpy(*new_prefix, cmp->prefix, len); + strncpy(*new_prefix+len, postfix, plen); +} + +GList* +g_completion_complete (GCompletion* cmp, gchar* prefix, gchar** new_prefix) { + gint plen, len; + gint done=0; + GList* list; + + g_return_val_if_fail(cmp != NULL, NULL); + g_return_val_if_fail(prefix != NULL, NULL); + + len = strlen(prefix); + if ( cmp->prefix && cmp->cache ) { + plen = strlen(cmp->prefix); + if ( plen <= len && !strncmp(prefix, cmp->prefix, plen) ) { + /* use the cache */ + list = cmp->cache; + while ( list ) { + if ( strncmp(prefix, cmp->func?(*cmp->func)(list->data):(gchar*)list->data, len) ) { + list = g_list_remove_link(cmp->cache, list); + if ( list != cmp->cache ) + cmp->cache = list; + } else + list = list->next; + } + done = 1; + } + } + + if (!done) { /* normal code */ + g_list_free(cmp->cache); + cmp->cache = NULL; + list = cmp->items; + while (*prefix && list) { + if ( !strncmp(prefix, cmp->func?(*cmp->func)(list->data):(gchar*)list->data, len) ) + cmp->cache = g_list_prepend(cmp->cache, list->data); + list = list->next; + } + } + if ( cmp->prefix ) { + g_free(cmp->prefix); + cmp->prefix = NULL; + } + if ( cmp->cache ) + cmp->prefix = g_strdup(prefix); + completion_check_cache(cmp, new_prefix); + return *prefix?cmp->cache:cmp->items; + +} + +void +g_completion_free (GCompletion* cmp) { + g_return_if_fail(cmp != NULL); + + g_completion_clear_items(cmp); + g_free(cmp); +} + +#ifdef TEST_COMPLETION + +#include + +int main (int argc, char* argv[]) { + + FILE * file; + gchar buf[1024]; + GList *list; + GList *result; + GList *tmp; + GCompletion * cmp; + gint i; + gchar* longp=NULL; + + if ( argc < 3 ) { + g_warning("Usage: %s filename prefix1 [prefix2 ...]\n", argv[0]); + return 1; + } + + if ( !(file=fopen(argv[1], "r")) ) { + g_warning("Cannot open %s\n", argv[1]); + return 1; + } + + cmp = g_completion_new(NULL); + list = g_list_alloc(); + while (fgets(buf, 1024, file)) { + list->data = g_strdup(buf); + g_completion_add_items(cmp, list); + } + fclose(file); + + for ( i= 2; i < argc; ++i) { + printf("COMPLETING: %s\n", argv[i]); + result = g_completion_complete(cmp, argv[i], &longp); + g_list_foreach(result, (GFunc)printf, NULL); + printf("LONG MATCH: %s\n", longp); + g_free(longp); + longp = NULL; + } + + g_list_foreach(cmp->items, (GFunc)g_free, NULL); + g_completion_free(cmp); + g_list_free(list); + return 0; +} + +#endif diff --git a/glib/glib.h b/glib/glib.h index 282288c977..92fba23a5f 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -1065,6 +1065,33 @@ void g_scanner_warn (GScanner *scanner, gint g_scanner_stat_mode (const gchar *filename); +/* Completion */ + +typedef gchar* (*GCompletionFunc)(gpointer); + +typedef struct _GCompletion GCompletion; + +struct _GCompletion { + GList* items; + GCompletionFunc func; + + gchar* prefix; + GList* cache; + +}; + +GCompletion* g_completion_new (GCompletionFunc func); +void g_completion_add_items (GCompletion* cmp, + GList* items); +void g_completion_remove_items (GCompletion* cmp, + GList* items); +void g_completion_clear_items (GCompletion* cmp); +GList* g_completion_complete (GCompletion* cmp, + gchar* prefix, + gchar** new_prefix); +void g_completion_free (GCompletion* cmp); + + #ifdef __cplusplus } diff --git a/gtk/gtkcombo.c b/gtk/gtkcombo.c index 0c2f7ccd49..8c8b3521c2 100644 --- a/gtk/gtkcombo.c +++ b/gtk/gtkcombo.c @@ -117,11 +117,28 @@ gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * com { GList *li; - /* completion? */ - /*if ( event->keyval == GDK_Tab ) { - gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event"); - return TRUE; - } else */ + /* completion */ + if ( event->keyval == GDK_Tab ) { + GCompletion * cmpl; + gchar* prefix; + gchar* nprefix = NULL; + guint pos; + + gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event"); + cmpl = g_completion_new((GCompletionFunc)gtk_combo_func); + g_completion_add_items(cmpl, GTK_LIST(combo->list)->children); + pos = GTK_EDITABLE(entry)->current_pos; + prefix = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, pos); + g_completion_complete(cmpl, prefix, &nprefix); + if ( nprefix && strlen(nprefix) > strlen(prefix) ) { + gtk_editable_insert_text(GTK_EDITABLE(entry), nprefix+pos, + strlen(nprefix)-strlen(prefix), &pos); + GTK_EDITABLE(entry)->current_pos = pos; + } + g_free(prefix); + g_completion_free(cmpl); + return TRUE; + } if (!combo->use_arrows || !GTK_LIST (combo->list)->children) return FALSE; li = g_list_find (GTK_LIST (combo->list)->children, gtk_combo_find (combo));