Re: Completion API and ComboBox API extension
- From: Kristian Rietveld <kris gtk org>
- To: Soeren Sandmann <sandmann daimi au dk>
- Cc: GTK Development list <gtk-devel-list gnome org>
- Subject: Re: Completion API and ComboBox API extension
- Date: 12 Jun 2003 15:42:08 +0200
On Thu, 2003-06-12 at 15:49, Soeren Sandmann wrote:
> On Thu, 2003-06-12 at 14:57, Kristian Rietveld wrote:
>
> > After a few good discussions with Owen, we came up with the following
> > ideas for the Completion API. I'll attach the headers and give some
> > explanation here.
>
> Could you send the patch to gtkentry.c too? It isn't entirely clear to
> me how the EntryCompletion object is used.
Sure. Though there are some pieces in gtkentry.c now that can be moved
to gtkentrycompletion.c (some bits of the GUI code). But I thought it
was better to put it in gtkentry.c. Maybe I am wrong though.
I also attached a simple testcase, to show how you would use the API in
real life(tm).
hope this helps,
-Kris
>
> S
Index: gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.219
diff -u -p -r1.219 gtkentry.c
--- gtkentry.c 29 Mar 2003 00:39:22 -0000 1.219
+++ gtkentry.c 12 Jun 2003 13:35:53 -0000
@@ -47,10 +47,15 @@
#include "gtkstock.h"
#include "gtktextutil.h"
#include "gtkwindow.h"
+#include "gtktreeview.h"
+#include "gtktreeselection.h"
+
+#define GTK_ENTRY_COMPLETION_KEY "gtk-entry-completion-key"
#define MIN_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
#define INNER_BORDER 2
+#define COMPLETION_TIMEOUT 300
/* Initial size of buffer, in bytes */
#define MIN_SIZE 16
@@ -307,6 +312,11 @@ static void get_widget_window_si
gint *width,
gint *height);
+/* completion */
+static void gtk_entry_completion_popdown (GtkEntryCompletion *completion);
+
+
+
static GtkWidgetClass *parent_class = NULL;
GType
@@ -1661,6 +1671,7 @@ gtk_entry_focus_out (GtkWidget *widg
GdkEventFocus *event)
{
GtkEntry *entry = GTK_ENTRY (widget);
+ GtkEntryCompletion *completion;
gtk_widget_queue_draw (widget);
@@ -1672,6 +1683,10 @@ gtk_entry_focus_out (GtkWidget *widg
g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
gtk_entry_keymap_direction_changed,
entry);
+
+ completion = g_object_get_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY);
+ if (completion)
+ gtk_entry_completion_popdown (completion);
return FALSE;
}
@@ -4437,4 +4452,272 @@ gtk_entry_pend_cursor_blink (GtkEntry *e
entry);
show_cursor (entry);
}
+}
+
+/* completion */
+static void
+gtk_entry_completion_popup (GtkEntryCompletion *completion)
+{
+ gint x, y, x_border, y_border;
+ gint items;
+ gint height;
+ GtkTreePath *path;
+
+ if (GTK_WIDGET_MAPPED (completion->popup_window))
+ return;
+
+ gtk_widget_show_all (completion->vbox);
+
+
+ gdk_window_get_origin (completion->entry->window, &x, &y);
+ get_borders (GTK_ENTRY (completion->entry), &x_border, &y_border);
+
+ items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->filter_model), NULL);
+
+ items = MIN (items, 15);
+
+ gtk_tree_view_column_cell_get_size (completion->column, NULL,
+ NULL, NULL, NULL, &height);
+
+ gtk_widget_set_size_request (completion->tree_view,
+ completion->entry->allocation.width - 2 * x_border,
+ items * height);
+
+ if (items <= 0)
+ gtk_widget_hide (completion->scrolled_window);
+
+ /* default on the first match */
+ path = gtk_tree_path_new_from_indices (0, -1);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->tree_view), path,
+ NULL, FALSE);
+ completion->current_match = 0;
+ gtk_tree_path_free (path);
+
+ items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->actions), NULL);
+
+ if (items)
+ {
+ gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->action_view), 0),
+ NULL, NULL, NULL, NULL,
+ &height);
+
+ gtk_widget_set_size_request (completion->action_view,
+ completion->entry->allocation.width - 2 * x_border,
+ items * height);
+ }
+
+ x += x_border;
+ y += 2 * y_border;
+
+ gtk_window_move (GTK_WINDOW (completion->popup_window), x, y + height);
+
+ gtk_widget_show (completion->popup_window);
+}
+
+static void
+gtk_entry_completion_popdown (GtkEntryCompletion *completion)
+{
+ gtk_widget_hide (completion->popup_window);
+}
+
+static gint
+gtk_entry_completion_timeout (gpointer data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+
+ GDK_THREADS_ENTER ();
+
+ completion->completion_timeout = 0;
+
+ if (strlen (gtk_entry_get_text (GTK_ENTRY (completion->entry))) >= completion->minimum_key_length)
+ {
+ gint matches;
+ gint actions;
+ GtkTreeSelection *s;
+
+ gtk_entry_completion_complete (completion);
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->filter_model), NULL);
+
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->tree_view)));
+
+ s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->action_view));
+
+ g_signal_handler_block (s, completion->action_changed_id);
+ gtk_tree_selection_unselect_all (s);
+ g_signal_handler_unblock (s, completion->action_changed_id);
+
+ actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->actions), NULL);
+
+ if (matches > 0 || actions > 0)
+ gtk_entry_completion_popup (completion);
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+static gboolean
+gtk_entry_completion_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (event->keyval == GDK_Up || event->keyval == GDK_Down)
+ {
+ gint matches;
+ GtkTreePath *path;
+
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->filter_model), NULL);
+
+ if (event->keyval == GDK_Up)
+ {
+ completion->current_match--;
+ if (completion->current_match < 0)
+ completion->current_match = matches - 1;
+ }
+ else
+ {
+ completion->current_match++;
+ if (completion->current_match >= matches)
+ completion->current_match = 0;
+ }
+
+ path = gtk_tree_path_new_from_indices (completion->current_match, -1);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->tree_view),
+ path, NULL, FALSE);
+ gtk_tree_path_free (path);
+
+ return TRUE;
+ }
+ else if (event->keyval == GDK_ISO_Enter ||
+ event->keyval == GDK_Return ||
+ event->keyval == GDK_Escape)
+ {
+ if (GTK_WIDGET_MAPPED (completion->popup_window))
+ {
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+ GtkTreeSelection *sel;
+ gboolean entry_set;
+
+ gtk_entry_completion_popdown (completion);
+
+ if (event->keyval == GDK_Escape)
+ return TRUE;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->tree_view));
+ gtk_tree_selection_get_selected (sel, &model, &iter);
+
+ g_signal_emit_by_name (completion, "match_selected",
+ model, &iter, &entry_set);
+
+ if (!entry_set)
+ {
+ gchar *str = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ completion->text_column, &str,
+ -1);
+
+ g_signal_handler_block (widget, completion->changed_id);
+ gtk_entry_set_text (GTK_ENTRY (widget), str);
+ g_signal_handler_unblock (widget, completion->changed_id);
+
+ /* move the cursor to the end */
+ gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+
+ g_free (str);
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_entry_completion_changed (GtkWidget *entry,
+ gpointer user_data)
+{
+ GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+ if (GTK_WIDGET_MAPPED (completion->popup_window))
+ gtk_entry_completion_popdown (completion);
+
+ /* (re)install completion timeout */
+ if (completion->completion_timeout)
+ g_source_remove (completion->completion_timeout);
+
+ if (!gtk_entry_get_text (GTK_ENTRY (entry)))
+ return;
+
+ /* no need to normalize for this test */
+ if (! strcmp ("", gtk_entry_get_text (GTK_ENTRY (entry))))
+ return;
+
+ completion->completion_timeout = g_timeout_add (COMPLETION_TIMEOUT,
+ gtk_entry_completion_timeout,
+ completion);
+}
+
+void
+gtk_entry_set_completion (GtkEntry *entry,
+ GtkEntryCompletion *completion)
+{
+ GtkEntryCompletion *old;
+
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+
+ old = gtk_entry_get_completion (entry);
+
+ if (old)
+ {
+ old->entry = NULL;
+
+ if (old->completion_timeout)
+ {
+ g_source_remove (old->completion_timeout);
+ old->completion_timeout = 0;
+ }
+
+ if (GTK_WIDGET_MAPPED (old->popup_window))
+ gtk_entry_completion_popup (old);
+
+ g_signal_handler_disconnect (entry, old->changed_id);
+ g_signal_handler_disconnect (entry, old->key_press_id);
+ }
+
+ if (!completion)
+ {
+ g_object_set_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY, NULL);
+ return;
+ }
+
+ /* hook into the entry */
+ completion->changed_id =
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (gtk_entry_completion_changed), completion);
+
+ completion->key_press_id =
+ g_signal_connect (entry, "key_press_event",
+ G_CALLBACK (gtk_entry_completion_key_press), completion);
+
+ completion->entry = GTK_WIDGET (entry);
+ g_object_set_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY, completion);
+}
+
+GtkEntryCompletion *
+gtk_entry_get_completion (GtkEntry *entry)
+{
+ GtkEntryCompletion *completion;
+
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ completion = GTK_ENTRY_COMPLETION (g_object_get_data (G_OBJECT (entry), GTK_ENTRY_COMPLETION_KEY));
+
+ return completion;
}
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#define DICT "/usr/share/dict/words"
static GtkTreeModel *
create_model (void)
{
gint count = 0;
GtkListStore *store;
GtkTreeIter iter;
FILE *file;
char buffer[64];
store = gtk_list_store_new (1, G_TYPE_STRING);
file = fopen (DICT, "ro");
if (!file) {
g_error ("Can't read from "DICT);
exit (EXIT_FAILURE);
}
while (fgets (buffer, 63, file)) {
buffer[strlen(buffer)-1] = 0;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, buffer, -1);
count++;
if (count % 2000 == 0)
g_print (".");
//if (count == 5000)
// break;
}
g_print ("\n");
fclose (file);
g_print ("%d strings\n", count);
return GTK_TREE_MODEL (store);
}
int
main (int argc, char **argv)
{
GtkWidget *window, *entry;
GtkTreeModel *model;
GtkEntryCompletion *completion;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
entry = gtk_entry_new ();
gtk_container_add (GTK_CONTAINER (window), entry);
model = create_model ();
completion = gtk_entry_completion_new ();
gtk_entry_completion_set_model (completion, model);
gtk_entry_completion_set_text_column (completion, 0);
//gtk_entry_completion_set_minimum_key_length (completion, 4);
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
/*
gtk_entry_set_text (GTK_ENTRY (entry), "acc");
gtk_entry_completion_complete (completion);
gtk_entry_set_text (GTK_ENTRY (entry), "blue");
gtk_entry_completion_complete (completion);
gtk_entry_set_text (GTK_ENTRY (entry), "zoo");
gtk_entry_completion_complete (completion);
gtk_entry_set_text (GTK_ENTRY (entry), "acci");
gtk_entry_completion_complete (completion);
*/
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]