Re: Completion API and ComboBox API extension



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]