[gtk+] Move entry completion code where it belongs



commit e5b2ca5d89f6defd1a12ec8f51c5ad4ba6adfbab
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Aug 31 10:47:23 2012 -0400

    Move entry completion code where it belongs
    
    This commit moves all the entry completion implementation
    into gtkentrycompletion.c. It also gets rid of an unnecessary
    completion_device member in GtkEntryPrivate.

 gtk/gtkentry.c           |  632 ++--------------------------------------------
 gtk/gtkentrycompletion.c |  564 ++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkentryprivate.h    |   10 +-
 3 files changed, 587 insertions(+), 619 deletions(-)
---
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 4de43e2..a2a5585 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -116,9 +116,7 @@
 
 #define MIN_ENTRY_WIDTH  150
 #define DRAW_TIMEOUT     20
-#define COMPLETION_TIMEOUT 300
 #define PASSWORD_HINT_MAX 8
-#define PAGE_STEP 14
 
 #define MAX_ICONS 2
 
@@ -146,7 +144,6 @@ struct _GtkEntryPrivate
 
   GdkDevice             *device;
 
-  GdkDevice             *completion_device;
   GdkWindow             *text_area;
 
   PangoLayout           *cached_layout;
@@ -576,29 +573,6 @@ static GdkPixbuf *  gtk_entry_ensure_pixbuf            (GtkEntry             *en
 static void         gtk_entry_update_cached_style_values(GtkEntry      *entry);
 static gboolean     get_middle_click_paste             (GtkEntry *entry);
 
-/* Completion */
-static gint         gtk_entry_completion_timeout       (gpointer            data);
-static gboolean     gtk_entry_completion_key_press     (GtkWidget          *widget,
-							GdkEventKey        *event,
-							gpointer            user_data);
-static void         gtk_entry_completion_changed       (GtkWidget          *entry,
-							gpointer            user_data);
-static gboolean     check_completion_callback          (GtkEntryCompletion *completion);
-static void         clear_completion_callback          (GtkEntry           *entry,
-							GParamSpec         *pspec);
-static gboolean     accept_completion_callback         (GtkEntry           *entry);
-static void         completion_insert_text_callback    (GtkEntry           *entry,
-							const gchar        *text,
-							gint                length,
-							gint                position,
-							GtkEntryCompletion *completion);
-static void         completion_changed                 (GtkEntryCompletion *completion,
-							GParamSpec         *pspec,
-							gpointer            data);
-static void         disconnect_completion_signals      (GtkEntry           *entry,
-							GtkEntryCompletion *completion);
-static void         connect_completion_signals         (GtkEntry           *entry,
-							GtkEntryCompletion *completion);
 
 static void         begin_change                       (GtkEntry *entry);
 static void         end_change                         (GtkEntry *entry);
@@ -2005,7 +1979,7 @@ gtk_entry_set_property (GObject         *object,
 
 	    if (!new_value)
 	      {
-		_gtk_entry_reset_im_context (entry);
+		gtk_entry_reset_im_context (entry);
 		if (gtk_widget_has_focus (widget))
 		  gtk_im_context_focus_out (priv->im_context);
 
@@ -2708,7 +2682,7 @@ gtk_entry_destroy (GtkWidget *widget)
   GtkEntryPrivate *priv = entry->priv;
 
   priv->current_pos = priv->selection_bound = 0;
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
   gtk_entry_reset_layout (entry);
 
   if (priv->blink_timeout)
@@ -2737,6 +2711,7 @@ gtk_entry_dispose (GObject *object)
   gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
   gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
   gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+  gtk_entry_set_completion (entry, NULL);
 
   if (priv->buffer)
     {
@@ -2776,8 +2751,6 @@ gtk_entry_finalize (GObject *object)
         }
     }
 
-  gtk_entry_set_completion (entry, NULL);
-
   if (priv->cached_layout)
     g_object_unref (priv->cached_layout);
 
@@ -3435,7 +3408,7 @@ gtk_entry_size_allocate (GtkWidget     *widget,
       gtk_entry_recompute (entry);
 
       completion = gtk_entry_get_completion (entry);
-      if (completion && gtk_widget_get_mapped (completion->priv->popup_window))
+      if (completion)
         _gtk_entry_completion_resize_popup (completion);
     }
 }
@@ -3928,11 +3901,11 @@ gtk_entry_button_press (GtkWidget      *widget,
           gtk_widget_get_modifier_mask (widget,
                                         GDK_MODIFIER_INTENT_EXTEND_SELECTION))
 	{
-	  _gtk_entry_reset_im_context (entry);
+	  gtk_entry_reset_im_context (entry);
 
 	  if (!have_selection) /* select from the current position to the clicked position */
 	    sel_start = sel_end = priv->current_pos;
-	  
+
 	  if (tmp_pos > sel_start && tmp_pos < sel_end)
 	    {
 	      /* Truncate current selection, but keep it as big as possible */
@@ -4311,21 +4284,11 @@ gtk_entry_key_press (GtkWidget   *widget,
 	}
     }
 
-  if (event->keyval == GDK_KEY_Return || 
-      event->keyval == GDK_KEY_KP_Enter || 
-      event->keyval == GDK_KEY_ISO_Enter || 
+  if (event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter ||
       event->keyval == GDK_KEY_Escape)
-    {
-      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-
-      if (completion && completion->priv->completion_timeout)
-        {
-          g_source_remove (completion->priv->completion_timeout);
-          completion->priv->completion_timeout = 0;
-        }
-
-      _gtk_entry_reset_im_context (entry);
-    }
+    gtk_entry_reset_im_context (entry);
 
   if (GTK_WIDGET_CLASS (gtk_entry_parent_class)->key_press_event (widget, event))
     /* Activate key bindings
@@ -4581,7 +4544,7 @@ gtk_entry_real_set_position (GtkEditable *editable,
   if (position != priv->current_pos ||
       position != priv->selection_bound)
     {
-      _gtk_entry_reset_im_context (entry);
+      gtk_entry_reset_im_context (entry);
       gtk_entry_set_positions (entry, position, position);
     }
 }
@@ -4608,8 +4571,8 @@ gtk_entry_set_selection_bounds (GtkEditable *editable,
     start = length;
   if (end < 0)
     end = length;
-  
-  _gtk_entry_reset_im_context (entry);
+
+  gtk_entry_reset_im_context (entry);
 
   gtk_entry_set_positions (entry,
 			   MIN (end, length),
@@ -4986,7 +4949,7 @@ gtk_entry_move_cursor (GtkEntry       *entry,
   GtkEntryPrivate *priv = entry->priv;
   gint new_pos = priv->current_pos;
 
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
 
   if (priv->current_pos != priv->selection_bound && !extend_selection)
     {
@@ -5103,8 +5066,7 @@ gtk_entry_insert_at_cursor (GtkEntry    *entry,
 
   if (priv->editable)
     {
-      _gtk_entry_reset_im_context (entry);
-
+      gtk_entry_reset_im_context (entry);
       gtk_editable_insert_text (editable, str, -1, &pos);
       gtk_editable_set_position (editable, pos);
     }
@@ -5120,8 +5082,8 @@ gtk_entry_delete_from_cursor (GtkEntry       *entry,
   gint start_pos = priv->current_pos;
   gint end_pos = priv->current_pos;
   gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry));
-  
-  _gtk_entry_reset_im_context (entry);
+
+  gtk_entry_reset_im_context (entry);
 
   if (!priv->editable)
     {
@@ -5198,7 +5160,7 @@ gtk_entry_backspace (GtkEntry *entry)
   GtkEditable *editable = GTK_EDITABLE (entry);
   gint prev_pos;
 
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
 
   if (!priv->editable)
     {
@@ -6020,18 +5982,6 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
     }
 }
 
-void
-_gtk_entry_reset_im_context (GtkEntry *entry)
-{
-  GtkEntryPrivate *priv = entry->priv;
-
-  if (priv->need_im_reset)
-    {
-      priv->need_im_reset = FALSE;
-      gtk_im_context_reset (priv->im_context);
-    }
-}
-
 /**
  * gtk_entry_reset_im_context:
  * @entry: a #GtkEntry
@@ -6046,9 +5996,15 @@ _gtk_entry_reset_im_context (GtkEntry *entry)
 void
 gtk_entry_reset_im_context (GtkEntry *entry)
 {
+  GtkEntryPrivate *priv = entry->priv;
+
   g_return_if_fail (GTK_IS_ENTRY (entry));
 
-  _gtk_entry_reset_im_context (entry);
+  if (priv->need_im_reset)
+    {
+      priv->need_im_reset = FALSE;
+      gtk_im_context_reset (priv->im_context);
+    }
 }
 
 /**
@@ -9406,510 +9362,6 @@ gtk_entry_reset_blink_time (GtkEntry *entry)
   priv->blink_time = 0;
 }
 
-
-/* completion */
-static gint
-gtk_entry_completion_timeout (gpointer data)
-{
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
-  GtkEntryPrivate *completion_entry_priv = GTK_ENTRY (completion->priv->entry)->priv;
-
-  completion->priv->completion_timeout = 0;
-
-  if (completion->priv->filter_model &&
-      g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)), -1)
-      >= completion->priv->minimum_key_length)
-    {
-      gint matches;
-      gint actions;
-      GtkTreeSelection *s;
-      gboolean popup_single;
-
-      gtk_entry_completion_complete (completion);
-      matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
-
-      gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-
-      s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
-
-      gtk_tree_selection_unselect_all (s);
-
-      actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
-
-      g_object_get (completion, "popup-single-match", &popup_single, NULL);
-      if ((matches > (popup_single ? 0: 1)) || actions > 0)
-	{ 
-	  if (gtk_widget_get_visible (completion->priv->popup_window))
-	    _gtk_entry_completion_resize_popup (completion);
-          else
-	    _gtk_entry_completion_popup (completion, completion_entry_priv->completion_device);
-	}
-      else 
-	_gtk_entry_completion_popdown (completion);
-    }
-  else if (gtk_widget_get_visible (completion->priv->popup_window))
-    _gtk_entry_completion_popdown (completion);
-
-  return FALSE;
-}
-
-static inline gboolean
-keyval_is_cursor_move (guint keyval)
-{
-  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Page_Up)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Page_Down)
-    return TRUE;
-
-  return FALSE;
-}
-
-static gboolean
-gtk_entry_completion_key_press (GtkWidget   *widget,
-                                GdkEventKey *event,
-                                gpointer     user_data)
-{
-  gint matches, actions = 0;
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
-
-  if (!gtk_widget_get_mapped (completion->priv->popup_window))
-    return FALSE;
-
-  matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
-
-  if (completion->priv->actions)
-    actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
-
-  if (keyval_is_cursor_move (event->keyval))
-    {
-      GtkTreePath *path = NULL;
-      
-      if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
-        {
-	  if (completion->priv->current_selected < 0)
-	    completion->priv->current_selected = matches + actions - 1;
-	  else
-	    completion->priv->current_selected--;
-        }
-      else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
-        {
-          if (completion->priv->current_selected < matches + actions - 1)
-	    completion->priv->current_selected++;
-	  else
-            completion->priv->current_selected = -1;
-        }
-      else if (event->keyval == GDK_KEY_Page_Up)
-	{
-	  if (completion->priv->current_selected < 0)
-	    completion->priv->current_selected = matches + actions - 1;
-	  else if (completion->priv->current_selected == 0)
-	    completion->priv->current_selected = -1;
-	  else if (completion->priv->current_selected < matches) 
-	    {
-	      completion->priv->current_selected -= PAGE_STEP;
-	      if (completion->priv->current_selected < 0)
-		completion->priv->current_selected = 0;
-	    }
-	  else 
-	    {
-	      completion->priv->current_selected -= PAGE_STEP;
-	      if (completion->priv->current_selected < matches - 1)
-		completion->priv->current_selected = matches - 1;
-	    }
-	}
-      else if (event->keyval == GDK_KEY_Page_Down)
-	{
-	  if (completion->priv->current_selected < 0)
-	    completion->priv->current_selected = 0;
-	  else if (completion->priv->current_selected < matches - 1)
-	    {
-	      completion->priv->current_selected += PAGE_STEP;
-	      if (completion->priv->current_selected > matches - 1)
-		completion->priv->current_selected = matches - 1;
-	    }
-	  else if (completion->priv->current_selected == matches + actions - 1)
-	    {
-	      completion->priv->current_selected = -1;
-	    }
-	  else
-	    {
-	      completion->priv->current_selected += PAGE_STEP;
-	      if (completion->priv->current_selected > matches + actions - 1)
-		completion->priv->current_selected = matches + actions - 1;
-	    }
-	}
-
-      if (completion->priv->current_selected < 0)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
-
-          if (completion->priv->inline_selection &&
-              completion->priv->completion_prefix)
-            {
-              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                  completion->priv->completion_prefix);
-              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-            }
-        }
-      else if (completion->priv->current_selected < matches)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
-
-          path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1);
-          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view),
-                                    path, NULL, FALSE);
-
-          if (completion->priv->inline_selection)
-            {
-
-              GtkTreeIter iter;
-              GtkTreeIter child_iter;
-              GtkTreeModel *model = NULL;
-              GtkTreeSelection *sel;
-              gboolean entry_set;
-
-              sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
-              if (!gtk_tree_selection_get_selected (sel, &model, &iter))
-                return FALSE;
-
-              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
-              model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
-              
-              if (completion->priv->completion_prefix == NULL)
-                completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)));
-
-              g_signal_emit_by_name (completion, "cursor-on-match", model,
-                                     &child_iter, &entry_set);
-            }
-        }
-      else if (completion->priv->current_selected - matches >= 0)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-
-          path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
-          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
-                                    path, NULL, FALSE);
-
-          if (completion->priv->inline_selection &&
-              completion->priv->completion_prefix)
-            {
-              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                  completion->priv->completion_prefix);
-              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-            }
-        }
-
-      gtk_tree_path_free (path);
-
-      return TRUE;
-    }
-  else if (event->keyval == GDK_KEY_Escape ||
-           event->keyval == GDK_KEY_Left ||
-           event->keyval == GDK_KEY_KP_Left ||
-           event->keyval == GDK_KEY_Right ||
-           event->keyval == GDK_KEY_KP_Right) 
-    {
-      gboolean retval = TRUE;
-
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      if (completion->priv->current_selected < 0)
-        {
-          retval = FALSE;
-          goto keypress_completion_out;
-        }
-      else if (completion->priv->inline_selection)
-        {
-          /* Escape rejects the tentative completion */
-          if (event->keyval == GDK_KEY_Escape)
-            {
-              if (completion->priv->completion_prefix)
-                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                    completion->priv->completion_prefix);
-              else 
-                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), "");
-            }
-
-          /* Move the cursor to the end for Right/Esc */
-          if (event->keyval == GDK_KEY_Right ||
-              event->keyval == GDK_KEY_KP_Right ||
-              event->keyval == GDK_KEY_Escape)
-            gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-          /* Let the default keybindings run for Left, i.e. either move to the
-           * previous character or select word if a modifier is used */
-          else
-            retval = FALSE;
-        }
-
-keypress_completion_out:
-      if (completion->priv->inline_selection)
-        {
-          g_free (completion->priv->completion_prefix);
-          completion->priv->completion_prefix = NULL;
-        }
-
-      return retval;
-    }
-  else if (event->keyval == GDK_KEY_Tab || 
-	   event->keyval == GDK_KEY_KP_Tab ||
-	   event->keyval == GDK_KEY_ISO_Left_Tab) 
-    {
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      g_free (completion->priv->completion_prefix);
-      completion->priv->completion_prefix = NULL;
-
-      return FALSE;
-    }
-  else if (event->keyval == GDK_KEY_ISO_Enter ||
-           event->keyval == GDK_KEY_KP_Enter ||
-	   event->keyval == GDK_KEY_Return)
-    {
-      GtkTreeIter iter;
-      GtkTreeModel *model = NULL;
-      GtkTreeModel *child_model;
-      GtkTreeIter child_iter;
-      GtkTreeSelection *sel;
-      gboolean retval = TRUE;
-
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      if (completion->priv->current_selected < matches)
-        {
-          gboolean entry_set;
-
-          sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
-          if (gtk_tree_selection_get_selected (sel, &model, &iter))
-            {
-              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
-              child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
-              g_signal_handler_block (widget, completion->priv->changed_id);
-              g_signal_emit_by_name (completion, "match-selected",
-                                     child_model, &child_iter, &entry_set);
-              g_signal_handler_unblock (widget, completion->priv->changed_id);
-
-              if (!entry_set)
-                {
-                  gchar *str = NULL;
-
-                  gtk_tree_model_get (model, &iter,
-                                      completion->priv->text_column, &str,
-                                      -1);
-
-                  gtk_entry_set_text (GTK_ENTRY (widget), str);
-
-                  /* move the cursor to the end */
-                  gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-
-                  g_free (str);
-                }
-            }
-          else
-            retval = FALSE;
-        }
-      else if (completion->priv->current_selected - matches >= 0)
-        {
-          sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
-          if (gtk_tree_selection_get_selected (sel, &model, &iter))
-            {
-              GtkTreePath *path;
-
-              path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
-              g_signal_emit_by_name (completion, "action-activated",
-                                     gtk_tree_path_get_indices (path)[0]);
-              gtk_tree_path_free (path);
-            }
-          else
-            retval = FALSE;
-        }
-
-      g_free (completion->priv->completion_prefix);
-      completion->priv->completion_prefix = NULL;
-
-      return retval;
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_entry_completion_changed (GtkWidget *widget,
-                              gpointer   user_data)
-{
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
-  GtkEntry *entry = GTK_ENTRY (widget);
-  GtkEntryPrivate *priv = entry->priv;
-  GdkDevice *device;
-
-  /* (re)install completion timeout */
-  if (completion->priv->completion_timeout)
-    g_source_remove (completion->priv->completion_timeout);
-
-  if (!gtk_entry_get_text (entry))
-    return;
-
-  /* no need to normalize for this test */
-  if (completion->priv->minimum_key_length > 0 &&
-      strcmp ("", gtk_entry_get_text (entry)) == 0)
-    {
-      if (gtk_widget_get_visible (completion->priv->popup_window))
-        _gtk_entry_completion_popdown (completion);
-      return;
-    }
-
-  device = gtk_get_current_event_device ();
-
-  if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
-    device = gdk_device_get_associated_device (device);
-
-  if (device)
-    priv->completion_device = device;
-
-  completion->priv->completion_timeout =
-    gdk_threads_add_timeout (COMPLETION_TIMEOUT,
-                   gtk_entry_completion_timeout,
-                   completion);
-}
-
-static gboolean
-check_completion_callback (GtkEntryCompletion *completion)
-{
-  completion->priv->check_completion_idle = NULL;
-  
-  gtk_entry_completion_complete (completion);
-  gtk_entry_completion_insert_prefix (completion);
-
-  return FALSE;
-}
-
-static void
-clear_completion_callback (GtkEntry   *entry,
-			   GParamSpec *pspec)
-{
-  if (pspec->name == I_("cursor-position") ||
-      pspec->name == I_("selection-bound"))
-    {
-      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-      
-      completion->priv->has_completion = FALSE;
-    }
-}
-
-static gboolean
-accept_completion_callback (GtkEntry *entry)
-{
-  GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-
-  if (completion->priv->has_completion)
-    gtk_editable_set_position (GTK_EDITABLE (entry),
-			       gtk_entry_buffer_get_length (get_buffer (entry)));
-
-  return FALSE;
-}
-
-static void
-completion_insert_text_callback (GtkEntry           *entry,
-				 const gchar        *text,
-				 gint                length,
-				 gint                position,
-				 GtkEntryCompletion *completion)
-{
-  /* idle to update the selection based on the file list */
-  if (completion->priv->check_completion_idle == NULL)
-    {
-      completion->priv->check_completion_idle = g_idle_source_new ();
-      g_source_set_priority (completion->priv->check_completion_idle, G_PRIORITY_HIGH);
-      g_source_set_closure (completion->priv->check_completion_idle,
-			    g_cclosure_new_object (G_CALLBACK (check_completion_callback),
-						   G_OBJECT (completion)));
-      g_source_attach (completion->priv->check_completion_idle, NULL);
-    }
-}
-
-static void
-completion_changed (GtkEntryCompletion *completion,
-		    GParamSpec         *pspec,
-		    gpointer            data)
-{
-  GtkEntry *entry = GTK_ENTRY (data);
-
-  if (pspec->name == I_("popup-completion") ||
-      pspec->name == I_("inline-completion"))
-    {
-      disconnect_completion_signals (entry, completion);
-      connect_completion_signals (entry, completion);
-    }
-}
-
-static void
-disconnect_completion_signals (GtkEntry           *entry,
-			       GtkEntryCompletion *completion)
-{
-  g_signal_handlers_disconnect_by_func (completion, 
-				       G_CALLBACK (completion_changed), entry);
-  if (completion->priv->changed_id > 0 &&
-      g_signal_handler_is_connected (entry, completion->priv->changed_id))
-    {
-      g_signal_handler_disconnect (entry, completion->priv->changed_id);
-      completion->priv->changed_id = 0;
-    }
-  g_signal_handlers_disconnect_by_func (entry, 
-					G_CALLBACK (gtk_entry_completion_key_press), completion);
-  if (completion->priv->insert_text_id > 0 &&
-      g_signal_handler_is_connected (entry, completion->priv->insert_text_id))
-    {
-      g_signal_handler_disconnect (entry, completion->priv->insert_text_id);
-      completion->priv->insert_text_id = 0;
-    }
-  g_signal_handlers_disconnect_by_func (entry, 
-					G_CALLBACK (completion_insert_text_callback), completion);
-  g_signal_handlers_disconnect_by_func (entry, 
-					G_CALLBACK (clear_completion_callback), completion);
-  g_signal_handlers_disconnect_by_func (entry, 
-					G_CALLBACK (accept_completion_callback), completion);
-}
-
-static void
-connect_completion_signals (GtkEntry           *entry,
-			    GtkEntryCompletion *completion)
-{
-  if (completion->priv->popup_completion)
-    {
-      completion->priv->changed_id =
-	g_signal_connect (entry, "changed",
-			  G_CALLBACK (gtk_entry_completion_changed), completion);
-      g_signal_connect (entry, "key-press-event",
-			G_CALLBACK (gtk_entry_completion_key_press), completion);
-    }
- 
-  if (completion->priv->inline_completion)
-    {
-      completion->priv->insert_text_id =
-	g_signal_connect (entry, "insert-text",
-			  G_CALLBACK (completion_insert_text_callback), completion);
-      g_signal_connect (entry, "notify",
-			G_CALLBACK (clear_completion_callback), completion);
-      g_signal_connect (entry, "activate",
-			G_CALLBACK (accept_completion_callback), completion);
-      g_signal_connect (entry, "focus-out-event",
-			G_CALLBACK (accept_completion_callback), completion);
-    }
-
-  g_signal_connect (completion, "notify",
-		    G_CALLBACK (completion_changed), entry);
-}
-
 /**
  * gtk_entry_set_completion:
  * @entry: A #GtkEntry
@@ -9938,24 +9390,7 @@ gtk_entry_set_completion (GtkEntry           *entry,
   
   if (old)
     {
-      if (old->priv->completion_timeout)
-        {
-          g_source_remove (old->priv->completion_timeout);
-          old->priv->completion_timeout = 0;
-        }
-
-      if (old->priv->check_completion_idle)
-        {
-          g_source_destroy (old->priv->check_completion_idle);
-          old->priv->check_completion_idle = NULL;
-        }
-
-      if (gtk_widget_get_mapped (old->priv->popup_window))
-        _gtk_entry_completion_popdown (old);
-
-      disconnect_completion_signals (entry, old);
-      old->priv->entry = NULL;
-
+      _gtk_entry_completion_disconnect (old);
       g_object_unref (old);
     }
 
@@ -9968,8 +9403,8 @@ gtk_entry_set_completion (GtkEntry           *entry,
   /* hook into the entry */
   g_object_ref (completion);
 
-  connect_completion_signals (entry, completion);    
-  completion->priv->entry = GTK_WIDGET (entry);
+  _gtk_entry_completion_connect (completion, entry);
+
   g_object_set_data (G_OBJECT (entry), I_(GTK_ENTRY_COMPLETION_KEY), completion);
 
   g_object_notify (G_OBJECT (entry), "completion");
@@ -10458,14 +9893,3 @@ gtk_entry_get_input_hints (GtkEntry *entry)
 
   return hints;
 }
-
-void
-gtk_entry_set_attributes (GtkEntry      *entry,
-                          PangoAttrList *attrs)
-{
-}
-
-PangoAttrList *
-gtk_entry_get_attributes (GtkEntry *attrs)
-{
-}
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index cd72d28..3e06c5f 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -87,6 +87,8 @@
 
 #include <string.h>
 
+#define PAGE_STEP 14
+#define COMPLETION_TIMEOUT 300
 
 /* signals */
 enum
@@ -177,6 +179,9 @@ static gboolean gtk_entry_completion_insert_completion   (GtkEntryCompletion *co
                                                           GtkTreeIter        *iter);
 static void     gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
                                                              const gchar *text);
+static void     connect_completion_signals                  (GtkEntryCompletion *completion);
+static void     disconnect_completion_signals               (GtkEntryCompletion *completion);
+
 
 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
 
@@ -985,7 +990,7 @@ gtk_entry_completion_action_button_press (GtkWidget      *widget,
   if (!gtk_widget_get_mapped (completion->priv->popup_window))
     return FALSE;
 
-  _gtk_entry_reset_im_context (GTK_ENTRY (completion->priv->entry));
+  gtk_entry_reset_im_context (GTK_ENTRY (completion->priv->entry));
 
   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
                                      event->x, event->y,
@@ -1487,6 +1492,9 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
   if (!window)
     return FALSE;
 
+  if (!completion->priv->filter_model)
+    return FALSE;
+
   gtk_widget_get_allocation (completion->priv->entry, &allocation);
   gtk_widget_get_preferred_size (completion->priv->entry,
                                  &entry_req, NULL);
@@ -1577,8 +1585,7 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
 }
 
 void
-_gtk_entry_completion_popup (GtkEntryCompletion *completion,
-                             GdkDevice          *device)
+_gtk_entry_completion_popup (GtkEntryCompletion *completion)
 {
   GtkTreeViewColumn *column;
   GtkStyleContext *context;
@@ -1595,7 +1602,7 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion,
   if (!gtk_widget_has_focus (completion->priv->entry))
     return;
 
-  if (completion->priv->grab_device)
+  if (completion->priv->has_grab)
     return;
 
   completion->priv->ignore_enter = TRUE;
@@ -1634,15 +1641,15 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion,
 
   gtk_widget_show (completion->priv->popup_window);
 
-  gtk_device_grab_add (completion->priv->popup_window, device, TRUE);
-  gdk_device_grab (device, gtk_widget_get_window (completion->priv->popup_window),
+  gtk_device_grab_add (completion->priv->popup_window, completion->priv->device, TRUE);
+  gdk_device_grab (completion->priv->device, gtk_widget_get_window (completion->priv->popup_window),
                    GDK_OWNERSHIP_WINDOW, TRUE,
                    GDK_BUTTON_PRESS_MASK |
                    GDK_BUTTON_RELEASE_MASK |
                    GDK_POINTER_MOTION_MASK,
                    NULL, GDK_CURRENT_TIME);
 
-  completion->priv->grab_device = device;
+  completion->priv->has_grab = TRUE;
 }
 
 void
@@ -1653,12 +1660,12 @@ _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
 
   completion->priv->ignore_enter = FALSE;
 
-  if (completion->priv->grab_device)
+  if (completion->priv->has_grab)
     {
-      gdk_device_ungrab (completion->priv->grab_device, GDK_CURRENT_TIME);
+      gdk_device_ungrab (completion->priv->device, GDK_CURRENT_TIME);
       gtk_device_grab_remove (completion->priv->popup_window,
-                              completion->priv->grab_device);
-      completion->priv->grab_device = NULL;
+                              completion->priv->device);
+      completion->priv->has_grab = FALSE;
     }
 
   gtk_widget_hide (completion->priv->popup_window);
@@ -1923,6 +1930,12 @@ gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
     {
       completion->priv->inline_completion = inline_completion;
 
+      if (completion->priv->entry)
+        {
+          disconnect_completion_signals (completion);
+          connect_completion_signals (completion);
+        }
+
       g_object_notify (G_OBJECT (completion), "inline-completion");
     }
 }
@@ -1967,6 +1980,12 @@ gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion,
     {
       completion->priv->popup_completion = popup_completion;
 
+      if (completion->priv->entry)
+        {
+          disconnect_completion_signals (completion);
+          connect_completion_signals (completion);
+        }
+
       g_object_notify (G_OBJECT (completion), "popup-completion");
     }
 }
@@ -2129,3 +2148,526 @@ gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion)
 
   return completion->priv->inline_selection;
 }
+
+
+static gint
+gtk_entry_completion_timeout (gpointer data)
+{
+  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+
+  completion->priv->completion_timeout = 0;
+
+  if (completion->priv->filter_model &&
+      g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)), -1)
+      >= completion->priv->minimum_key_length)
+    {
+      gint matches;
+      gint actions;
+      GtkTreeSelection *s;
+      gboolean popup_single;
+
+      gtk_entry_completion_complete (completion);
+      matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+      gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
+
+      s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
+
+      gtk_tree_selection_unselect_all (s);
+
+      actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+
+      g_object_get (completion, "popup-single-match", &popup_single, NULL);
+      if ((matches > (popup_single ? 0: 1)) || actions > 0)
+        {
+          if (gtk_widget_get_visible (completion->priv->popup_window))
+            _gtk_entry_completion_resize_popup (completion);
+          else
+            _gtk_entry_completion_popup (completion);
+        }
+      else
+        _gtk_entry_completion_popdown (completion);
+    }
+  else if (gtk_widget_get_visible (completion->priv->popup_window))
+    _gtk_entry_completion_popdown (completion);
+
+  return FALSE;
+}
+
+static inline gboolean
+keyval_is_cursor_move (guint keyval)
+{
+  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
+    return TRUE;
+
+  if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
+    return TRUE;
+
+  if (keyval == GDK_KEY_Page_Up)
+    return TRUE;
+
+  if (keyval == GDK_KEY_Page_Down)
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+gtk_entry_completion_key_press (GtkWidget   *widget,
+                                GdkEventKey *event,
+                                gpointer     user_data)
+{
+  gint matches, actions = 0;
+  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+
+  if (event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter ||
+      event->keyval == GDK_KEY_Escape)
+    {
+      if (completion && completion->priv->completion_timeout)
+        {
+          g_source_remove (completion->priv->completion_timeout);
+          completion->priv->completion_timeout = 0;
+        }
+    }
+
+  if (!gtk_widget_get_mapped (completion->priv->popup_window))
+    return FALSE;
+
+  matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+
+  if (completion->priv->actions)
+    actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+
+  if (keyval_is_cursor_move (event->keyval))
+    {
+      GtkTreePath *path = NULL;
+
+      if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
+        {
+          if (completion->priv->current_selected < 0)
+            completion->priv->current_selected = matches + actions - 1;
+          else
+            completion->priv->current_selected--;
+        }
+      else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
+        {
+          if (completion->priv->current_selected < matches + actions - 1)
+            completion->priv->current_selected++;
+          else
+            completion->priv->current_selected = -1;
+        }
+      else if (event->keyval == GDK_KEY_Page_Up)
+        {
+          if (completion->priv->current_selected < 0)
+            completion->priv->current_selected = matches + actions - 1;
+          else if (completion->priv->current_selected == 0)
+            completion->priv->current_selected = -1;
+          else if (completion->priv->current_selected < matches)
+            {
+              completion->priv->current_selected -= PAGE_STEP;
+              if (completion->priv->current_selected < 0)
+                completion->priv->current_selected = 0;
+            }
+          else
+            {
+              completion->priv->current_selected -= PAGE_STEP;
+              if (completion->priv->current_selected < matches - 1)
+                completion->priv->current_selected = matches - 1;
+            }
+        }
+      else if (event->keyval == GDK_KEY_Page_Down)
+        {
+          if (completion->priv->current_selected < 0)
+            completion->priv->current_selected = 0;
+          else if (completion->priv->current_selected < matches - 1)
+            {
+              completion->priv->current_selected += PAGE_STEP;
+              if (completion->priv->current_selected > matches - 1)
+                completion->priv->current_selected = matches - 1;
+            }
+          else if (completion->priv->current_selected == matches + actions - 1)
+            {
+              completion->priv->current_selected = -1;
+            }
+          else
+            {
+              completion->priv->current_selected += PAGE_STEP;
+              if (completion->priv->current_selected > matches + actions - 1)
+                completion->priv->current_selected = matches + actions - 1;
+            }
+        }
+
+      if (completion->priv->current_selected < 0)
+        {
+          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
+          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
+
+          if (completion->priv->inline_selection &&
+              completion->priv->completion_prefix)
+            {
+              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry),
+                                  completion->priv->completion_prefix);
+              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+            }
+        }
+      else if (completion->priv->current_selected < matches)
+        {
+          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
+
+          path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1);
+          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view),
+                                    path, NULL, FALSE);
+
+          if (completion->priv->inline_selection)
+            {
+
+              GtkTreeIter iter;
+              GtkTreeIter child_iter;
+              GtkTreeModel *model = NULL;
+              GtkTreeSelection *sel;
+              gboolean entry_set;
+
+              sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
+              if (!gtk_tree_selection_get_selected (sel, &model, &iter))
+                return FALSE;
+             gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+              model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+
+              if (completion->priv->completion_prefix == NULL)
+                completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)));
+
+              g_signal_emit_by_name (completion, "cursor-on-match", model,
+                                     &child_iter, &entry_set);
+            }
+        }
+      else if (completion->priv->current_selected - matches >= 0)
+        {
+          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
+
+          path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
+          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
+                                    path, NULL, FALSE);
+
+          if (completion->priv->inline_selection &&
+              completion->priv->completion_prefix)
+            {
+              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry),
+                                  completion->priv->completion_prefix);
+              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+            }
+        }
+
+      gtk_tree_path_free (path);
+
+      return TRUE;
+    }
+  else if (event->keyval == GDK_KEY_Escape ||
+           event->keyval == GDK_KEY_Left ||
+           event->keyval == GDK_KEY_KP_Left ||
+           event->keyval == GDK_KEY_Right ||
+           event->keyval == GDK_KEY_KP_Right)
+    {
+      gboolean retval = TRUE;
+
+      gtk_entry_reset_im_context (GTK_ENTRY (widget));
+      _gtk_entry_completion_popdown (completion);
+
+      if (completion->priv->current_selected < 0)
+        {
+          retval = FALSE;
+          goto keypress_completion_out;
+        }
+      else if (completion->priv->inline_selection)
+        {
+          /* Escape rejects the tentative completion */
+          if (event->keyval == GDK_KEY_Escape)
+            {
+              if (completion->priv->completion_prefix)
+                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry),
+                                    completion->priv->completion_prefix);
+              else
+                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), "");
+            }
+
+          /* Move the cursor to the end for Right/Esc */
+          if (event->keyval == GDK_KEY_Right ||
+              event->keyval == GDK_KEY_KP_Right ||
+              event->keyval == GDK_KEY_Escape)
+            gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+          /* Let the default keybindings run for Left, i.e. either move to the
+ *            * previous character or select word if a modifier is used */
+          else
+            retval = FALSE;
+        }
+
+keypress_completion_out:
+      if (completion->priv->inline_selection)
+        {
+          g_free (completion->priv->completion_prefix);
+          completion->priv->completion_prefix = NULL;
+        }
+
+      return retval;
+    }
+  else if (event->keyval == GDK_KEY_Tab ||
+           event->keyval == GDK_KEY_KP_Tab ||
+           event->keyval == GDK_KEY_ISO_Left_Tab)
+    {
+      gtk_entry_reset_im_context (GTK_ENTRY (widget));
+      _gtk_entry_completion_popdown (completion);
+
+      g_free (completion->priv->completion_prefix);
+      completion->priv->completion_prefix = NULL;
+
+      return FALSE;
+    }
+  else if (event->keyval == GDK_KEY_ISO_Enter ||
+           event->keyval == GDK_KEY_KP_Enter ||
+           event->keyval == GDK_KEY_Return)
+    {
+      GtkTreeIter iter;
+      GtkTreeModel *model = NULL;
+      GtkTreeModel *child_model;
+      GtkTreeIter child_iter;
+      GtkTreeSelection *sel;
+      gboolean retval = TRUE;
+
+      gtk_entry_reset_im_context (GTK_ENTRY (widget));
+      _gtk_entry_completion_popdown (completion);
+
+      if (completion->priv->current_selected < matches)
+        {
+          gboolean entry_set;
+
+          sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
+          if (gtk_tree_selection_get_selected (sel, &model, &iter))
+            {
+              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+              child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+              g_signal_handler_block (widget, completion->priv->changed_id);
+              g_signal_emit_by_name (completion, "match-selected",
+                                     child_model, &child_iter, &entry_set);
+              g_signal_handler_unblock (widget, completion->priv->changed_id);
+
+              if (!entry_set)
+                {
+                  gchar *str = NULL;
+
+                  gtk_tree_model_get (model, &iter,
+                                      completion->priv->text_column, &str,
+                                      -1);
+
+                  gtk_entry_set_text (GTK_ENTRY (widget), str);
+
+                  /* move the cursor to the end */
+                  gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+                  g_free (str);
+                }
+            }
+          else
+            retval = FALSE;
+        }
+      else if (completion->priv->current_selected - matches >= 0)
+        {
+          sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
+          if (gtk_tree_selection_get_selected (sel, &model, &iter))
+            {
+              GtkTreePath *path;
+
+              path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
+              g_signal_emit_by_name (completion, "action-activated",
+                                     gtk_tree_path_get_indices (path)[0]);
+              gtk_tree_path_free (path);
+            }
+          else
+            retval = FALSE;
+        }
+
+      g_free (completion->priv->completion_prefix);
+      completion->priv->completion_prefix = NULL;
+
+      return retval;
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_entry_completion_changed (GtkWidget *widget,
+                              gpointer   user_data)
+{
+  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+  GtkEntry *entry = GTK_ENTRY (widget);
+  GdkDevice *device;
+
+  /* (re)install completion timeout */
+  if (completion->priv->completion_timeout)
+    g_source_remove (completion->priv->completion_timeout);
+
+  if (!gtk_entry_get_text (entry))
+    return;
+
+  /* no need to normalize for this test */
+  if (completion->priv->minimum_key_length > 0 &&
+      strcmp ("", gtk_entry_get_text (entry)) == 0)
+    {
+      if (gtk_widget_get_visible (completion->priv->popup_window))
+        _gtk_entry_completion_popdown (completion);
+      return;
+    }
+
+  device = gtk_get_current_event_device ();
+
+  if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+    device = gdk_device_get_associated_device (device);
+
+  if (device)
+    completion->priv->device = device;
+
+  completion->priv->completion_timeout =
+    gdk_threads_add_timeout (COMPLETION_TIMEOUT,
+                   gtk_entry_completion_timeout,
+                   completion);
+}
+
+static gboolean
+check_completion_callback (GtkEntryCompletion *completion)
+{
+  completion->priv->check_completion_idle = NULL;
+  
+  gtk_entry_completion_complete (completion);
+  gtk_entry_completion_insert_prefix (completion);
+
+  return FALSE;
+}
+
+static void
+clear_completion_callback (GtkEntry   *entry,
+                           GParamSpec *pspec)
+{
+  if (pspec->name == I_("cursor-position") ||
+      pspec->name == I_("selection-bound"))
+    {
+      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
+      
+      completion->priv->has_completion = FALSE;
+    }
+}
+
+static gboolean
+accept_completion_callback (GtkEntry *entry)
+{
+  GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
+
+  if (completion->priv->has_completion)
+    gtk_editable_set_position (GTK_EDITABLE (entry),
+                               gtk_entry_buffer_get_length (gtk_entry_get_buffer (entry)));
+
+  return FALSE;
+}
+
+static void
+completion_insert_text_callback (GtkEntry           *entry,
+                                 const gchar        *text,
+                                 gint                length,
+                                 gint                position,
+                                 GtkEntryCompletion *completion)
+{
+  /* idle to update the selection based on the file list */
+  if (completion->priv->check_completion_idle == NULL)
+    {
+      completion->priv->check_completion_idle = g_idle_source_new ();
+      g_source_set_priority (completion->priv->check_completion_idle, G_PRIORITY_HIGH);
+      g_source_set_closure (completion->priv->check_completion_idle,
+                            g_cclosure_new_object (G_CALLBACK (check_completion_callback),
+                                                   G_OBJECT (completion)));
+      g_source_attach (completion->priv->check_completion_idle, NULL);
+    }
+}
+
+static void
+connect_completion_signals (GtkEntryCompletion *completion)
+{
+  if (completion->priv->popup_completion)
+    {
+      completion->priv->changed_id =
+        g_signal_connect (completion->priv->entry, "changed",
+                          G_CALLBACK (gtk_entry_completion_changed), completion);
+      g_signal_connect (completion->priv->entry, "key-press-event",
+                        G_CALLBACK (gtk_entry_completion_key_press), completion);
+    }
+
+  if (completion->priv->inline_completion)
+    {
+      completion->priv->insert_text_id =
+        g_signal_connect (completion->priv->entry, "insert-text",
+                          G_CALLBACK (completion_insert_text_callback), completion);
+      g_signal_connect (completion->priv->entry, "notify",
+                        G_CALLBACK (clear_completion_callback), completion);
+      g_signal_connect (completion->priv->entry, "activate",
+                        G_CALLBACK (accept_completion_callback), completion);
+      g_signal_connect (completion->priv->entry, "focus-out-event",
+                        G_CALLBACK (accept_completion_callback), completion);
+    }
+}
+
+
+static void
+disconnect_completion_signals (GtkEntryCompletion *completion)
+{
+  if (completion->priv->changed_id > 0 &&
+      g_signal_handler_is_connected (completion->priv->entry,
+                                     completion->priv->changed_id))
+    {
+      g_signal_handler_disconnect (completion->priv->entry,
+                                   completion->priv->changed_id);
+      completion->priv->changed_id = 0;
+    }
+  g_signal_handlers_disconnect_by_func (completion->priv->entry,
+                                        G_CALLBACK (gtk_entry_completion_key_press), completion);
+  if (completion->priv->insert_text_id > 0 &&
+      g_signal_handler_is_connected (completion->priv->entry,
+                                     completion->priv->insert_text_id))
+    {
+      g_signal_handler_disconnect (completion->priv->entry,
+                                   completion->priv->insert_text_id);
+      completion->priv->insert_text_id = 0;
+    }
+  g_signal_handlers_disconnect_by_func (completion->priv->entry,
+                                        G_CALLBACK (completion_insert_text_callback), completion);
+  g_signal_handlers_disconnect_by_func (completion->priv->entry,
+                                        G_CALLBACK (clear_completion_callback), completion);
+  g_signal_handlers_disconnect_by_func (completion->priv->entry,
+                                        G_CALLBACK (accept_completion_callback), completion);
+}
+
+void
+_gtk_entry_completion_disconnect (GtkEntryCompletion *completion)
+{
+  if (completion->priv->completion_timeout)
+    {
+      g_source_remove (completion->priv->completion_timeout);
+      completion->priv->completion_timeout = 0;
+    }
+  if (completion->priv->check_completion_idle)
+    {
+      g_source_destroy (completion->priv->check_completion_idle);
+      completion->priv->check_completion_idle = NULL;
+    }
+
+  if (gtk_widget_get_mapped (completion->priv->popup_window))
+    _gtk_entry_completion_popdown (completion);
+
+  disconnect_completion_signals (completion);
+
+  completion->priv->entry = NULL;
+}
+
+void
+_gtk_entry_completion_connect (GtkEntryCompletion *completion,
+                               GtkEntry           *entry)
+{
+  completion->priv->entry = GTK_WIDGET (entry);
+  connect_completion_signals (completion);
+}
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index f43e93a..0cc825a 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -64,29 +64,31 @@ struct _GtkEntryCompletionPrivate
   guint popup_set_width   : 1;
   guint popup_single_match : 1;
   guint inline_selection   : 1;
+  guint has_grab           : 1;
 
   gchar *completion_prefix;
 
   GSource *check_completion_idle;
 
-  GdkDevice *grab_device;
+  GdkDevice *device;
 };
 
 gboolean _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion);
-void     _gtk_entry_completion_popup        (GtkEntryCompletion *completion,
-                                             GdkDevice          *device);
 void     _gtk_entry_completion_popdown      (GtkEntryCompletion *completion);
+void     _gtk_entry_completion_connect      (GtkEntryCompletion *completion,
+                                             GtkEntry           *entry);
+void     _gtk_entry_completion_disconnect   (GtkEntryCompletion *completion);
 
 gchar*   _gtk_entry_get_display_text       (GtkEntry *entry,
                                             gint      start_pos,
                                             gint      end_pos);
 void     _gtk_entry_get_borders            (GtkEntry  *entry,
                                             GtkBorder *borders);
-void     _gtk_entry_reset_im_context       (GtkEntry  *entry);
 GtkIMContext* _gtk_entry_get_im_context    (GtkEntry  *entry);
 void     _gtk_entry_set_is_cell_renderer   (GtkEntry  *entry,
                                             gboolean   is_cell_renderer);
 
+
 G_END_DECLS
 
 #endif /* __GTK_ENTRY_PRIVATE_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]