]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkentrycompletion.c
Updated Bulgarian translation by Alexander Shopov <ash@contact.bg>
[~andy/gtk] / gtk / gtkentrycompletion.c
index a63277fb28d5d668b3eb5aeccb25373be1c03c3d..5cae4ab142e6b65aee9155e792afc199cb333cb9 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
+#include "gtkalias.h"
 #include "gtkentrycompletion.h"
 #include "gtkentryprivate.h"
 #include "gtkcelllayout.h"
 
 #include "gtkintl.h"
 #include "gtkcellrenderertext.h"
+#include "gtkframe.h"
 #include "gtktreeselection.h"
 #include "gtktreeview.h"
 #include "gtkscrolledwindow.h"
@@ -39,6 +42,7 @@
 /* signals */
 enum
 {
+  INSERT_PREFIX,
   MATCH_SELECTED,
   ACTION_ACTIVATED,
   LAST_SIGNAL
@@ -49,9 +53,13 @@ enum
 {
   PROP_0,
   PROP_MODEL,
-  PROP_MINIMUM_KEY_LENGTH
+  PROP_MINIMUM_KEY_LENGTH,
+  PROP_TEXT_COLUMN,
+  PROP_INLINE_COMPLETION,
+  PROP_POPUP_COMPLETION
 };
 
+#define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
 
 static void     gtk_entry_completion_class_init          (GtkEntryCompletionClass *klass);
 static void     gtk_entry_completion_cell_layout_init    (GtkCellLayoutIface      *iface);
@@ -84,6 +92,9 @@ static void     gtk_entry_completion_set_cell_data_func  (GtkCellLayout
                                                           GDestroyNotify           destroy);
 static void     gtk_entry_completion_clear_attributes    (GtkCellLayout           *cell_layout,
                                                           GtkCellRenderer         *cell);
+static void     gtk_entry_completion_reorder             (GtkCellLayout           *cell_layout,
+                                                          GtkCellRenderer         *cell,
+                                                          gint                     position);
 
 static gboolean gtk_entry_completion_visible_func        (GtkTreeModel            *model,
                                                           GtkTreeIter             *iter,
@@ -102,7 +113,12 @@ static gboolean gtk_entry_completion_action_button_press (GtkWidget
                                                           gpointer                 user_data);
 static void     gtk_entry_completion_selection_changed   (GtkTreeSelection        *selection,
                                                           gpointer                 data);
-
+static gboolean        gtk_entry_completion_list_enter_notify   (GtkWidget               *widget,
+                                                         GdkEventCrossing        *event,
+                                                         gpointer                 data);
+static gboolean gtk_entry_completion_list_motion_notify         (GtkWidget               *widget,
+                                                         GdkEventMotion          *event,
+                                                         gpointer                 data);
 static void     gtk_entry_completion_insert_action       (GtkEntryCompletion      *completion,
                                                           gint                     index,
                                                           const gchar             *string,
@@ -113,7 +129,13 @@ static void     gtk_entry_completion_action_data_func    (GtkTreeViewColumn
                                                           GtkTreeIter             *iter,
                                                           gpointer                 data);
 
+static gboolean gtk_entry_completion_match_selected      (GtkEntryCompletion *completion,
+                                                         GtkTreeModel       *model,
+                                                         GtkTreeIter        *iter);
+static gboolean gtk_entry_completion_real_insert_prefix  (GtkEntryCompletion *completion,
+                                                         const gchar        *prefix);
 
+static GObjectClass *parent_class = NULL;
 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
 
 
@@ -161,12 +183,59 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
 {
   GObjectClass *object_class;
 
+  parent_class = g_type_class_peek_parent (klass);
   object_class = (GObjectClass *)klass;
 
   object_class->set_property = gtk_entry_completion_set_property;
   object_class->get_property = gtk_entry_completion_get_property;
   object_class->finalize = gtk_entry_completion_finalize;
 
+  klass->match_selected = gtk_entry_completion_match_selected;
+  klass->insert_prefix = gtk_entry_completion_real_insert_prefix;
+
+  /**
+   * GtkEntryCompletion::insert-prefix:
+   * @widget: the object which received the signal
+   * @prefix: the common prefix of all possible completions
+   * 
+   * Gets emitted when the inline autocompletion is triggered. 
+   * The default behaviour is to make the entry display the 
+   * whole prefix and select the newly inserted part.
+   *
+   * Applications may connect to this signal in order to insert only a
+   * smaller part of the @prefix into the entry - e.g. the entry used in
+   * the #GtkFileChooser inserts only the part of the prefix up to the 
+   * next '/'.
+   *
+   * Return value: %TRUE if the signal has been handled
+   * 
+   * Since: 2.6
+   */ 
+  entry_completion_signals[INSERT_PREFIX] =
+    g_signal_new ("insert_prefix",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkEntryCompletionClass, insert_prefix),
+                  _gtk_boolean_handled_accumulator, NULL,
+                  _gtk_marshal_BOOLEAN__STRING,
+                  G_TYPE_BOOLEAN, 1,
+                  G_TYPE_STRING);
+
+  /**
+   * GtkEntryCompletion::match-selected:
+   * @widget: the object which received the signal
+   * @model: the #GtkTreeModel containing the matches
+   * @iter: a #GtkTreeIter positioned at the selected match
+   * 
+   * Gets emitted when a match from the list is selected. 
+   * The default behaviour is to replace the contents of the 
+   * entry with the contents of the text column in the row 
+   * pointed to by @iter.
+   *
+   * Return value: %TRUE if the signal has been handled
+   * 
+   * Since: 2.4
+   */ 
   entry_completion_signals[MATCH_SELECTED] =
     g_signal_new ("match_selected",
                   G_TYPE_FROM_CLASS (klass),
@@ -177,6 +246,16 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
                   G_TYPE_BOOLEAN, 2,
                   GTK_TYPE_TREE_MODEL,
                   GTK_TYPE_TREE_ITER);
+                 
+  /**
+   * GtkEntryCompletion::action-activated:
+   * @widget: the object which received the signal
+   * @index: the index of the activated action
+   *
+   * Gets emitted when an action is activated.
+   * 
+   * Since: 2.4
+   */
   entry_completion_signals[ACTION_ACTIVATED] =
     g_signal_new ("action_activated",
                   G_TYPE_FROM_CLASS (klass),
@@ -190,19 +269,66 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
   g_object_class_install_property (object_class,
                                    PROP_MODEL,
                                    g_param_spec_object ("model",
-                                                        _("Completion Model"),
-                                                        _("The model to find matches in"),
+                                                        P_("Completion Model"),
+                                                        P_("The model to find matches in"),
                                                         GTK_TYPE_TREE_MODEL,
                                                         G_PARAM_READWRITE));
   g_object_class_install_property (object_class,
                                    PROP_MINIMUM_KEY_LENGTH,
                                    g_param_spec_int ("minimum_key_length",
-                                                     _("Minimum Key Length"),
-                                                     _("Minimum length of the search key in order to look up matches"),
+                                                     P_("Minimum Key Length"),
+                                                     P_("Minimum length of the search key in order to look up matches"),
                                                      -1,
                                                      G_MAXINT,
                                                      1,
                                                      G_PARAM_READWRITE));
+  /**
+   * GtkEntryCompletion:text-column:
+   *
+   * The column of the model containing the strings.
+   *
+   * Since: 2.6
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_TEXT_COLUMN,
+                                   g_param_spec_int ("text_column",
+                                                     P_("Text column"),
+                                                     P_("The column of the model containing the strings."),
+                                                     -1,
+                                                     G_MAXINT,
+                                                     -1,
+                                                     G_PARAM_READWRITE));
+
+  /**
+   * GtkEntryCompletion:inline-completion:
+   * 
+   * Determines whether the common prefix of the possible completions 
+   * should be inserted automatically in the entry.
+   *
+   * Since: 2.6
+   **/
+  g_object_class_install_property (object_class,
+                                  PROP_INLINE_COMPLETION,
+                                  g_param_spec_boolean ("inline_completion",
+                                                        P_("Inline completion"),
+                                                        P_("Whether the common prefix should be inserted automatically"),
+                                                        FALSE,
+                                                        G_PARAM_READWRITE));
+  /**
+   * GtkEntryCompletion:popup-completion:
+   * 
+   * Determines whether the possible completions should be 
+   * shown in a popup window. 
+   *
+   * Since: 2.6
+   **/
+  g_object_class_install_property (object_class,
+                                  PROP_POPUP_COMPLETION,
+                                  g_param_spec_boolean ("popup_completion",
+                                                        P_("Popup completion"),
+                                                        P_("Whether the completions should be shown in a popup window"),
+                                                        TRUE,
+                                                        G_PARAM_READWRITE));
 
   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
 }
@@ -216,6 +342,7 @@ gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
   iface->add_attribute = gtk_entry_completion_add_attribute;
   iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
   iface->clear_attributes = gtk_entry_completion_clear_attributes;
+  iface->reorder = gtk_entry_completion_reorder;
 }
 
 static void
@@ -224,12 +351,16 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
   GtkCellRenderer *cell;
   GtkTreeSelection *sel;
   GtkEntryCompletionPrivate *priv;
+  GtkWidget *popup_frame;
 
   /* yes, also priv, need to keep the code readable */
   priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
 
   priv->minimum_key_length = 1;
   priv->text_column = -1;
+  priv->has_completion = FALSE;
+  priv->inline_completion = FALSE;
+  priv->popup_completion = TRUE;
 
   /* completions */
   priv->filter_model = NULL;
@@ -238,7 +369,14 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
   g_signal_connect (priv->tree_view, "button_press_event",
                     G_CALLBACK (gtk_entry_completion_list_button_press),
                     completion);
+  g_signal_connect (priv->tree_view, "enter_notify_event",
+                   G_CALLBACK (gtk_entry_completion_list_enter_notify),
+                   completion);
+  g_signal_connect (priv->tree_view, "motion_notify_event",
+                   G_CALLBACK (gtk_entry_completion_list_motion_notify),
+                   completion);
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+  gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
 
   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
@@ -256,8 +394,10 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
                                   GTK_POLICY_NEVER,
                                   GTK_POLICY_AUTOMATIC);
   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
-                                       GTK_SHADOW_ETCHED_IN);
+                                       GTK_SHADOW_NONE);
 
+  /* a nasty hack to get the completions treeview to size nicely */
+  gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
 
   /* actions */
   priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
@@ -267,16 +407,20 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
   g_signal_connect (priv->action_view, "button_press_event",
                     G_CALLBACK (gtk_entry_completion_action_button_press),
                     completion);
+  g_signal_connect (priv->action_view, "enter_notify_event",
+                   G_CALLBACK (gtk_entry_completion_list_enter_notify),
+                   completion);
+  g_signal_connect (priv->action_view, "motion_notify_event",
+                   G_CALLBACK (gtk_entry_completion_list_motion_notify),
+                   completion);
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
+  gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->action_view), TRUE);
 
   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
   gtk_tree_selection_unselect_all (sel);
 
   cell = gtk_cell_renderer_text_new ();
-  g_object_set (cell, "cell_background_gdk",
-                &priv->tree_view->style->bg[GTK_STATE_NORMAL],
-                NULL);
   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
                                               0, "",
                                               cell,
@@ -294,8 +438,14 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
                     G_CALLBACK (gtk_entry_completion_popup_button_press),
                     completion);
 
+  popup_frame = gtk_frame_new (NULL);
+  gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
+                            GTK_SHADOW_ETCHED_IN);
+  gtk_widget_show (popup_frame);
+  gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
+  
   priv->vbox = gtk_vbox_new (FALSE, 0);
-  gtk_container_add (GTK_CONTAINER (priv->popup_window), priv->vbox);
+  gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
 
   gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
@@ -314,6 +464,7 @@ gtk_entry_completion_set_property (GObject      *object,
                                    GParamSpec   *pspec)
 {
   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
+  GtkEntryCompletionPrivate *priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
 
   switch (prop_id)
     {
@@ -327,6 +478,18 @@ gtk_entry_completion_set_property (GObject      *object,
                                                      g_value_get_int (value));
         break;
 
+      case PROP_TEXT_COLUMN:
+       priv->text_column = g_value_get_int (value);
+        break;
+
+      case PROP_INLINE_COMPLETION:
+       priv->inline_completion = g_value_get_boolean (value);
+        break;
+
+      case PROP_POPUP_COMPLETION:
+       priv->popup_completion = g_value_get_boolean (value);
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -352,6 +515,18 @@ gtk_entry_completion_get_property (GObject    *object,
         g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
         break;
 
+      case PROP_TEXT_COLUMN:
+        g_value_set_int (value, gtk_entry_completion_get_text_column (completion));
+        break;
+
+      case PROP_INLINE_COMPLETION:
+        g_value_set_boolean (value, gtk_entry_completion_get_inline_completion (completion));
+        break;
+
+      case PROP_POPUP_COMPLETION:
+        g_value_set_boolean (value, gtk_entry_completion_get_popup_completion (completion));
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -377,6 +552,8 @@ gtk_entry_completion_finalize (GObject *object)
 
   if (completion->priv->popup_window)
     gtk_widget_destroy (completion->priv->popup_window);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 /* implement cell layout interface */
@@ -465,6 +642,20 @@ gtk_entry_completion_clear_attributes (GtkCellLayout   *cell_layout,
   gtk_tree_view_column_clear_attributes (priv->column, cell);
 }
 
+static void
+gtk_entry_completion_reorder (GtkCellLayout   *cell_layout,
+                              GtkCellRenderer *cell,
+                              gint             position)
+{
+  GtkEntryCompletionPrivate *priv;
+
+  g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
+
+  priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
+
+  gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
+}
+
 /* all those callbacks */
 static gboolean
 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
@@ -482,19 +673,25 @@ gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
 
   model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
 
+  g_return_val_if_fail (gtk_tree_model_get_column_type (model, completion->priv->text_column) == G_TYPE_STRING, 
+                       FALSE);
+
   gtk_tree_model_get (model, iter,
                       completion->priv->text_column, &item,
                       -1);
 
-  normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
-  case_normalized_string = g_utf8_casefold (normalized_string, -1);
-
-  if (!strncmp (key, case_normalized_string, strlen (key)))
-      ret = TRUE;
-
-  g_free (item);
-  g_free (normalized_string);
-  g_free (case_normalized_string);
+  if (item != NULL)
+    {
+      normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
+      case_normalized_string = g_utf8_casefold (normalized_string, -1);
+      
+      if (!strncmp (key, case_normalized_string, strlen (key)))
+       ret = TRUE;
+      
+      g_free (item);
+      g_free (normalized_string);
+      g_free (case_normalized_string);
+    }
 
   return ret;
 }
@@ -511,17 +708,16 @@ gtk_entry_completion_visible_func (GtkTreeModel *model,
   if (!completion->priv->case_normalized_key)
     return ret;
 
-  if (completion->priv->text_column >= 0)
-    ret = gtk_entry_completion_default_completion_func (completion,
-                                                        completion->priv->case_normalized_key,
-                                                        iter,
-                                                        NULL);
-
-  else if (completion->priv->match_func)
+  if (completion->priv->match_func)
     ret = (* completion->priv->match_func) (completion,
                                             completion->priv->case_normalized_key,
                                             iter,
                                             completion->priv->match_data);
+  else if (completion->priv->text_column >= 0)
+    ret = gtk_entry_completion_default_completion_func (completion,
+                                                        completion->priv->case_normalized_key,
+                                                        iter,
+                                                        NULL);
 
   return ret;
 }
@@ -573,40 +769,23 @@ gtk_entry_completion_list_button_press (GtkWidget      *widget,
                                      event->x, event->y,
                                      &path, NULL, NULL, NULL))
     {
-      gboolean entry_set;
       GtkTreeIter iter;
+      gboolean entry_set;
 
       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
                                &iter, path);
       gtk_tree_path_free (path);
 
+      g_signal_handler_block (completion->priv->entry,
+                             completion->priv->changed_id);
       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
                      0, GTK_TREE_MODEL (completion->priv->filter_model),
                      &iter, &entry_set);
-
-      if (!entry_set)
-        {
-          gchar *str = NULL;
-
-          gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
-                              &iter,
-                              completion->priv->text_column, &str,
-                              -1);
-
-          g_signal_handler_block (completion->priv->entry,
-                                  completion->priv->changed_id);
-          gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
-          g_signal_handler_unblock (completion->priv->entry,
-                                    completion->priv->changed_id);
-
-          /* move cursor to the end */
-          gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry),
-                                     -1);
-
-          g_free (str);
-        }
+      g_signal_handler_unblock (completion->priv->entry,
+                               completion->priv->changed_id);
 
       _gtk_entry_completion_popdown (completion);
+
       return TRUE;
     }
 
@@ -658,15 +837,17 @@ gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
     return;
 
   if (markup)
-    g_object_set (G_OBJECT (cell),
+    g_object_set (cell,
                   "text", NULL,
                   "markup", string,
                   NULL);
   else
-    g_object_set (G_OBJECT (cell),
+    g_object_set (cell,
                   "markup", NULL,
                   "text", string,
                   NULL);
+
+  g_free (string);
 }
 
 static void
@@ -678,7 +859,8 @@ gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
   if (completion->priv->first_sel_changed)
     {
       completion->priv->first_sel_changed = FALSE;
-      gtk_tree_selection_unselect_all (selection);
+      if (gtk_widget_is_focus (completion->priv->tree_view))
+        gtk_tree_selection_unselect_all (selection);
     }
 }
 
@@ -728,6 +910,7 @@ gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
  *
  * Sets the model for a #GtkEntryCompletion. If @completion already has
  * a model set, it will remove it before setting the new model.
+ * If model is %NULL, then it will unset the model.
  *
  * Since: 2.4
  */
@@ -736,8 +919,17 @@ gtk_entry_completion_set_model (GtkEntryCompletion *completion,
                                 GtkTreeModel       *model)
 {
   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
-  g_return_if_fail (GTK_IS_TREE_MODEL (model));
+  g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
 
+  if (!model)
+    {
+      gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
+                              NULL);
+      _gtk_entry_completion_popdown (completion);
+      completion->priv->filter_model = NULL;
+      return;
+    }
+     
   /* code will unref the old filter model (if any) */
   completion->priv->filter_model =
     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
@@ -745,9 +937,13 @@ gtk_entry_completion_set_model (GtkEntryCompletion *completion,
                                           gtk_entry_completion_visible_func,
                                           completion,
                                           NULL);
+
   gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
                            GTK_TREE_MODEL (completion->priv->filter_model));
-  g_object_unref (G_OBJECT (completion->priv->filter_model));
+  g_object_unref (completion->priv->filter_model);
+
+  if (GTK_WIDGET_VISIBLE (completion->priv->popup_window))
+    _gtk_entry_completion_resize_popup (completion);
 }
 
 /**
@@ -766,6 +962,9 @@ gtk_entry_completion_get_model (GtkEntryCompletion *completion)
 {
   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
 
+  if (!completion->priv->filter_model)
+    return NULL;
+  
   return gtk_tree_model_filter_get_model (completion->priv->filter_model);
 }
 
@@ -854,8 +1053,10 @@ gtk_entry_completion_complete (GtkEntryCompletion *completion)
   gchar *tmp;
 
   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
-  g_return_if_fail (completion->priv->filter_model != NULL);
 
+  if (!completion->priv->filter_model)
+    return;
+  
   if (completion->priv->case_normalized_key)
     g_free (completion->priv->case_normalized_key);
 
@@ -898,10 +1099,10 @@ gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
 /**
  * gtk_entry_completion_insert_action_text:
  * @completion: A #GtkEntryCompletion.
- * @index: The index of the item to insert.
+ * @index_: The index of the item to insert.
  * @text: Text of the item to insert.
  *
- * Inserts an action in @completion's action item list at position @index
+ * Inserts an action in @completion's action item list at position @index_
  * with text @text. If you want the action item to have markup, use
  * gtk_entry_completion_insert_action_markup().
  *
@@ -909,57 +1110,57 @@ gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
  */
 void
 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
-                                         gint                index,
+                                         gint                index_,
                                          const gchar        *text)
 {
   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
   g_return_if_fail (text != NULL);
 
-  gtk_entry_completion_insert_action (completion, index, text, FALSE);
+  gtk_entry_completion_insert_action (completion, index_, text, FALSE);
 }
 
 /**
  * gtk_entry_completion_insert_action_markup:
  * @completion: A #GtkEntryCompletion.
- * @index: The index of the item to insert.
+ * @index_: The index of the item to insert.
  * @markup: Markup of the item to insert.
  *
- * Inserts an action in @completion's action item list at position @index
+ * Inserts an action in @completion's action item list at position @index_
  * with markup @markup.
  *
  * Since: 2.4
  */
 void
 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
-                                           gint                index,
+                                           gint                index_,
                                            const gchar        *markup)
 {
   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
   g_return_if_fail (markup != NULL);
 
-  gtk_entry_completion_insert_action (completion, index, markup, TRUE);
+  gtk_entry_completion_insert_action (completion, index_, markup, TRUE);
 }
 
 /**
  * gtk_entry_completion_delete_action:
  * @completion: A #GtkEntryCompletion.
- * @index: The index of the item to Delete.
+ * @index_: The index of the item to Delete.
  *
- * Deletes the action at @index from @completion's action list.
+ * Deletes the action at @index_ from @completion's action list.
  *
  * Since: 2.4
  */
 void
 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
-                                    gint                index)
+                                    gint                index_)
 {
   GtkTreeIter iter;
 
   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
-  g_return_if_fail (index >= 0);
+  g_return_if_fail (index_ >= 0);
 
   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
-                                 &iter, NULL, index);
+                                 &iter, NULL, index_);
   gtk_list_store_remove (completion->priv->actions, &iter);
 }
 
@@ -968,11 +1169,15 @@ gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
  * @completion: A #GtkEntryCompletion.
  * @column: The column in the model of @completion to get strings from.
  *
- * Conviencefunction for setting up the most used case of this code: a
+ * Convenience function for setting up the most used case of this code: a
  * completion list with just strings. This function will set up @completion
  * to have a list displaying all (and just) strings in the completion list,
  * and to get those strings from @column in the model of @completion.
  *
+ * This functions creates and adds a #GtkCellRendererText for the selected 
+ * column. If you need to set the text column, but don't want the cell 
+ * renderer, use g_object_set() to set the ::text_column property directly.
+ * 
  * Since: 2.4
  */
 void
@@ -992,72 +1197,93 @@ gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
                                  cell,
                                  "text", column);
-}
 
-/* private */
+  g_object_notify (G_OBJECT (completion), "text_column");
+}
 
-/* lame copy from gtkentry.c */
-static void
-get_borders (GtkEntry *entry,
-             gint     *xborder,
-             gint     *yborder)
+/**
+ * gtk_entry_completion_get_text_column:
+ * @completion: a #GtkEntryCompletion
+ * 
+ * Returns the column in the model of @completion to get strings from.
+ * 
+ * Return value: the column containing the strings
+ *
+ * Since: 2.6
+ **/
+gint
+gtk_entry_completion_get_text_column (GtkEntryCompletion *completion)
 {
-  GtkWidget *widget = GTK_WIDGET (entry);
-  gint focus_width;
-  gboolean interior_focus;
+  g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), -1);
 
-  gtk_widget_style_get (widget,
-                        "interior-focus", &interior_focus,
-                        "focus-line-width", &focus_width,
-                        NULL);
+  return completion->priv->text_column;  
+}
 
-  if (entry->has_frame)
-    {
-      *xborder = widget->style->xthickness;
-      *yborder = widget->style->ythickness;
-    }
-  else
-    {
-      *xborder = 0;
-      *yborder = 0;
-    }
+/* private */
 
-  if (!interior_focus)
-    {
-      *xborder += focus_width;
-      *yborder += focus_width;
-    }
+static gboolean
+gtk_entry_completion_list_enter_notify (GtkWidget        *widget,
+                                       GdkEventCrossing *event,
+                                       gpointer          data)
+{
+  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+  
+  return completion->priv->ignore_enter;
 }
 
-/* this function is a bit nasty */
-void
-_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+static gboolean
+gtk_entry_completion_list_motion_notify (GtkWidget      *widget,
+                                        GdkEventMotion *event,
+                                        gpointer        data)
 {
-  gint x, y, x_border, y_border;
-  gint items;
-  gint height;
+  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
 
-  if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
-    return;
+  completion->priv->ignore_enter = FALSE; 
+  
+  return FALSE;
+}
 
-  gtk_widget_show_all (completion->priv->vbox);
+
+/* some nasty size requisition */
+gboolean
+_gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
+{
+  gint x, y;
+  gint matches, items, height, x_border, y_border;
+  GdkScreen *screen;
+  gint monitor_num;
+  GdkRectangle monitor;
+  GtkRequisition popup_req;
+  GtkRequisition entry_req;
+  GtkTreePath *path;
+  gboolean above;
+  gint width;
+
+  if (!completion->priv->entry->window)
+    return FALSE;
 
   gdk_window_get_origin (completion->priv->entry->window, &x, &y);
-  get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
+  _gtk_entry_get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
 
-  items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+  matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
 
-  items = MIN (items, 15);
+  items = MIN (matches, 15);
 
   gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
                                       NULL, NULL, NULL, &height);
 
-  gtk_widget_set_size_request (completion->priv->tree_view,
-                               completion->priv->entry->allocation.width - 2 * x_border,
-                               items * height);
-
   if (items <= 0)
     gtk_widget_hide (completion->priv->scrolled_window);
+  else
+    gtk_widget_show (completion->priv->scrolled_window);
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
+  monitor_num = gdk_screen_get_monitor_at_window (screen, 
+                                                 GTK_WIDGET (completion->priv->entry)->window);
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+  width = MIN (completion->priv->entry->allocation.width, monitor.width) - 2 * x_border;
+  gtk_widget_set_size_request (completion->priv->tree_view, width, items * height);
 
   /* default on no match */
   completion->priv->current_selected = -1;
@@ -1066,22 +1292,72 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
 
   if (items)
     {
-      gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0),
-                                          NULL, NULL, NULL, NULL,
-                                          &height);
-
-      gtk_widget_set_size_request (completion->priv->action_view,
-                                   completion->priv->entry->allocation.width - 2 * x_border,
-                                   items * height);
+      gtk_widget_show (completion->priv->action_view);
+      gtk_widget_set_size_request (completion->priv->action_view, width, -1);
+    }
+  else
+    gtk_widget_hide (completion->priv->action_view);
+
+  gtk_widget_size_request (completion->priv->popup_window, &popup_req);
+  gtk_widget_size_request (completion->priv->entry, &entry_req);
+  
+  if (x < monitor.x)
+    x = monitor.x;
+  else if (x + popup_req.width > monitor.x + monitor.width)
+    x = monitor.x + monitor.width - popup_req.width;
+  
+  if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height)
+    {
+      y += entry_req.height;
+      above = FALSE;
+    }
+  else
+    {
+      y -= popup_req.height;
+      above = TRUE;
+    }
+  
+  if (matches > 0) 
+    {
+      path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
+      gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path, 
+                                   NULL, FALSE, 0.0, 0.0);
+      gtk_tree_path_free (path);
     }
 
-  x += x_border;
-  y += 2 * y_border;
+  gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
 
-  gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y + height);
+  return above;
+}
 
-  gtk_widget_show (completion->priv->popup_window);
+void
+_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+{
+  GtkTreeViewColumn *column;
+  GList *renderers;
+
+  if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
+    return;
+
+  if (!GTK_WIDGET_MAPPED (completion->priv->entry))
+    return;
 
+  completion->priv->ignore_enter = TRUE;
+    
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
+  renderers = gtk_tree_view_column_get_cell_renderers (column);
+  gtk_widget_ensure_style (completion->priv->tree_view);
+  g_object_set (GTK_CELL_RENDERER (renderers->data), "cell_background_gdk",
+                &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
+                NULL);
+  g_list_free (renderers);
+
+  gtk_widget_show_all (completion->priv->vbox);
+
+  _gtk_entry_completion_resize_popup (completion);
+
+  gtk_widget_show (completion->priv->popup_window);
+    
   gtk_grab_add (completion->priv->popup_window);
   gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
                     GDK_BUTTON_PRESS_MASK |
@@ -1093,8 +1369,227 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
 void
 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
 {
+  if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
+    return;
+
+  completion->priv->ignore_enter = FALSE;
+  
   gdk_pointer_ungrab (GDK_CURRENT_TIME);
   gtk_grab_remove (completion->priv->popup_window);
 
   gtk_widget_hide (completion->priv->popup_window);
 }
+
+static gboolean 
+gtk_entry_completion_match_selected (GtkEntryCompletion *completion,
+                                    GtkTreeModel       *model,
+                                    GtkTreeIter        *iter)
+{
+  gchar *str = NULL;
+
+  gtk_tree_model_get (model, iter, completion->priv->text_column, &str, -1);
+  gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
+  
+  /* move cursor to the end */
+  gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry), -1);
+  
+  g_free (str);
+
+  return TRUE;
+}
+
+
+static gchar *
+gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion)
+{
+  GtkTreeIter iter;
+  gchar *prefix = NULL;
+  gboolean valid;
+
+  const gchar *key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
+
+  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (completion->priv->filter_model),
+                                        &iter);
+  
+  while (valid)
+    {
+      gchar *text;
+      
+      gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
+                         &iter, completion->priv->text_column, &text,
+                         -1);
+
+      if (g_str_has_prefix (text, key))
+       {
+         if (!prefix)
+           prefix = g_strdup (text);
+         else
+           {
+             gchar *p = prefix;
+             const gchar *q = text;
+             
+             while (*p && *p == *q)
+               {
+                 p++;
+                 q++;
+               }
+             
+             *p = '\0';
+           }
+       }
+      
+      g_free (text);
+      valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (completion->priv->filter_model),
+                                       &iter);
+    }
+
+  return prefix;
+}
+
+
+static gboolean
+gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
+                                        const gchar        *prefix)
+{
+  if (prefix)
+    {
+      gint key_len;
+      gint prefix_len;
+      gint pos;
+      const gchar *key;
+
+      prefix_len = g_utf8_strlen (prefix, -1);
+
+      key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
+      key_len = g_utf8_strlen (key, -1);
+
+      if (prefix_len > key_len)
+       {
+         gtk_editable_insert_text (GTK_EDITABLE (completion->priv->entry),
+                                   prefix + key_len, -1, &pos);
+         gtk_editable_select_region (GTK_EDITABLE (completion->priv->entry),
+                                     key_len, prefix_len);
+
+         completion->priv->has_completion = TRUE;
+       }
+    }
+
+  return TRUE;
+}
+
+/**
+ * gtk_entry_completion_insert_prefix:
+ * @completion: a #GtkEntryCompletion
+ * 
+ * Requests a prefix insertion. 
+ * 
+ * Since: 2.6
+ **/
+void
+gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
+{
+  gboolean done;
+  gchar *prefix;
+
+  g_signal_handler_block (completion->priv->entry,
+                         completion->priv->insert_text_id);
+  prefix = gtk_entry_completion_compute_prefix (completion);
+  if (prefix)
+    {
+      g_signal_emit (completion, entry_completion_signals[INSERT_PREFIX],
+                    0, prefix, &done);
+      g_free (prefix);
+    }
+  g_signal_handler_unblock (completion->priv->entry,
+                           completion->priv->insert_text_id);
+}
+
+/**
+ * gtk_entry_completion_set_inline_completion:
+ * @completion: a #GtkEntryCompletion
+ * @inline_completion: %TRUE to do inline completion
+ * 
+ * Sets whether the common prefix of the possible completions should
+ * be automatically inserted in the entry.
+ * 
+ * Since: 2.6
+ **/
+void 
+gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
+                                           gboolean            inline_completion)
+{
+  g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+  
+  inline_completion = inline_completion != FALSE;
+
+  if (completion->priv->inline_completion != inline_completion)
+    {
+      completion->priv->inline_completion = inline_completion;
+
+      g_object_notify (G_OBJECT (completion), "inline_completion");
+    }
+}
+
+
+/**
+ * gtk_entry_completion_get_inline_completion:
+ * @completion: a #GtkEntryCompletion
+ * 
+ * Returns whether the common prefix of the possible completions should
+ * be automatically inserted in the entry.
+ * 
+ * Return value: %TRUE if inline completion is turned on
+ * 
+ * Since: 2.6
+ **/
+gboolean
+gtk_entry_completion_get_inline_completion (GtkEntryCompletion *completion)
+{
+  g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
+  
+  return completion->priv->inline_completion;
+}
+
+/**
+ * gtk_entry_completion_set_popup_completion:
+ * @completion: a #GtkEntryCompletion
+ * @popup_completion: %TRUE to do popup completion
+ * 
+ * Sets whether the completions should be presented in a popup window.
+ * 
+ * Since: 2.6
+ **/
+void 
+gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion,
+                                          gboolean            popup_completion)
+{
+  g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+  
+  popup_completion = popup_completion != FALSE;
+
+  if (completion->priv->popup_completion != popup_completion)
+    {
+      completion->priv->popup_completion = popup_completion;
+
+      g_object_notify (G_OBJECT (completion), "popup_completion");
+    }
+}
+
+
+/**
+ * gtk_entry_completion_get_popup_completion:
+ * @completion: a #GtkEntryCompletion
+ * 
+ * Returns whether the completions should be presented in a popup window.
+ * 
+ * Return value: %TRUE if popup completion is turned on
+ * 
+ * Since: 2.6
+ **/
+gboolean
+gtk_entry_completion_get_popup_completion (GtkEntryCompletion *completion)
+{
+  g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
+  
+  return completion->priv->popup_completion;
+}