]> Pileus Git - ~andy/gtk/blob - gtk/gtkentrycompletion.c
Don't allow -1 as minimum-key-length. (gtk_entry_completion_set_model):
[~andy/gtk] / gtk / gtkentrycompletion.c
1 /* gtkentrycompletion.c
2  * Copyright (C) 2003  Kristian Rietveld  <kris@gtk.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21 #include "gtkalias.h"
22 #include "gtkentrycompletion.h"
23 #include "gtkentryprivate.h"
24 #include "gtkcelllayout.h"
25
26 #include "gtkintl.h"
27 #include "gtkcellrenderertext.h"
28 #include "gtkframe.h"
29 #include "gtktreeselection.h"
30 #include "gtktreeview.h"
31 #include "gtkscrolledwindow.h"
32 #include "gtkvbox.h"
33 #include "gtkwindow.h"
34 #include "gtkentry.h"
35 #include "gtkmain.h"
36 #include "gtksignal.h"
37 #include "gtkmarshalers.h"
38
39 #include <string.h>
40
41
42 /* signals */
43 enum
44 {
45   INSERT_PREFIX,
46   MATCH_SELECTED,
47   ACTION_ACTIVATED,
48   LAST_SIGNAL
49 };
50
51 /* properties */
52 enum
53 {
54   PROP_0,
55   PROP_MODEL,
56   PROP_MINIMUM_KEY_LENGTH,
57   PROP_TEXT_COLUMN,
58   PROP_INLINE_COMPLETION,
59   PROP_POPUP_COMPLETION
60 };
61
62 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
63
64 static void     gtk_entry_completion_class_init          (GtkEntryCompletionClass *klass);
65 static void     gtk_entry_completion_cell_layout_init    (GtkCellLayoutIface      *iface);
66 static void     gtk_entry_completion_init                (GtkEntryCompletion      *completion);
67 static void     gtk_entry_completion_set_property        (GObject                 *object,
68                                                           guint                    prop_id,
69                                                           const GValue            *value,
70                                                           GParamSpec              *pspec);
71 static void     gtk_entry_completion_get_property        (GObject                 *object,
72                                                           guint                    prop_id,
73                                                           GValue                  *value,
74                                                           GParamSpec              *pspec);
75 static void     gtk_entry_completion_finalize            (GObject                 *object);
76
77 static void     gtk_entry_completion_pack_start          (GtkCellLayout           *cell_layout,
78                                                           GtkCellRenderer         *cell,
79                                                           gboolean                 expand);
80 static void     gtk_entry_completion_pack_end            (GtkCellLayout           *cell_layout,
81                                                           GtkCellRenderer         *cell,
82                                                           gboolean                 expand);
83 static void     gtk_entry_completion_clear               (GtkCellLayout           *cell_layout);
84 static void     gtk_entry_completion_add_attribute       (GtkCellLayout           *cell_layout,
85                                                           GtkCellRenderer         *cell,
86                                                           const char              *attribute,
87                                                           gint                     column);
88 static void     gtk_entry_completion_set_cell_data_func  (GtkCellLayout           *cell_layout,
89                                                           GtkCellRenderer         *cell,
90                                                           GtkCellLayoutDataFunc    func,
91                                                           gpointer                 func_data,
92                                                           GDestroyNotify           destroy);
93 static void     gtk_entry_completion_clear_attributes    (GtkCellLayout           *cell_layout,
94                                                           GtkCellRenderer         *cell);
95 static void     gtk_entry_completion_reorder             (GtkCellLayout           *cell_layout,
96                                                           GtkCellRenderer         *cell,
97                                                           gint                     position);
98
99 static gboolean gtk_entry_completion_visible_func        (GtkTreeModel            *model,
100                                                           GtkTreeIter             *iter,
101                                                           gpointer                 data);
102 static gboolean gtk_entry_completion_popup_key_press     (GtkWidget               *widget,
103                                                           GdkEventKey             *event,
104                                                           gpointer                 user_data);
105 static gboolean gtk_entry_completion_popup_button_press  (GtkWidget               *widget,
106                                                           GdkEventButton          *event,
107                                                           gpointer                 user_data);
108 static gboolean gtk_entry_completion_list_button_press   (GtkWidget               *widget,
109                                                           GdkEventButton          *event,
110                                                           gpointer                 user_data);
111 static gboolean gtk_entry_completion_action_button_press (GtkWidget               *widget,
112                                                           GdkEventButton          *event,
113                                                           gpointer                 user_data);
114 static void     gtk_entry_completion_selection_changed   (GtkTreeSelection        *selection,
115                                                           gpointer                 data);
116 static gboolean gtk_entry_completion_list_enter_notify   (GtkWidget               *widget,
117                                                           GdkEventCrossing        *event,
118                                                           gpointer                 data);
119 static gboolean gtk_entry_completion_list_motion_notify  (GtkWidget               *widget,
120                                                           GdkEventMotion          *event,
121                                                           gpointer                 data);
122 static void     gtk_entry_completion_insert_action       (GtkEntryCompletion      *completion,
123                                                           gint                     index,
124                                                           const gchar             *string,
125                                                           gboolean                 markup);
126 static void     gtk_entry_completion_action_data_func    (GtkTreeViewColumn       *tree_column,
127                                                           GtkCellRenderer         *cell,
128                                                           GtkTreeModel            *model,
129                                                           GtkTreeIter             *iter,
130                                                           gpointer                 data);
131
132 static gboolean gtk_entry_completion_match_selected      (GtkEntryCompletion *completion,
133                                                           GtkTreeModel       *model,
134                                                           GtkTreeIter        *iter);
135 static gboolean gtk_entry_completion_real_insert_prefix  (GtkEntryCompletion *completion,
136                                                           const gchar        *prefix);
137
138 static GObjectClass *parent_class = NULL;
139 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
140
141
142 GType
143 gtk_entry_completion_get_type (void)
144 {
145   static GType entry_completion_type = 0;
146
147   if (!entry_completion_type)
148     {
149       static const GTypeInfo entry_completion_info =
150       {
151         sizeof (GtkEntryCompletionClass),
152         NULL,
153         NULL,
154         (GClassInitFunc) gtk_entry_completion_class_init,
155         NULL,
156         NULL,
157         sizeof (GtkEntryCompletion),
158         0,
159         (GInstanceInitFunc) gtk_entry_completion_init
160       };
161
162       static const GInterfaceInfo cell_layout_info =
163       {
164         (GInterfaceInitFunc) gtk_entry_completion_cell_layout_init,
165         NULL,
166         NULL
167       };
168
169       entry_completion_type =
170         g_type_register_static (G_TYPE_OBJECT, "GtkEntryCompletion",
171                                 &entry_completion_info, 0);
172
173       g_type_add_interface_static (entry_completion_type,
174                                    GTK_TYPE_CELL_LAYOUT,
175                                    &cell_layout_info);
176     }
177
178   return entry_completion_type;
179 }
180
181 static void
182 gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
183 {
184   GObjectClass *object_class;
185
186   parent_class = g_type_class_peek_parent (klass);
187   object_class = (GObjectClass *)klass;
188
189   object_class->set_property = gtk_entry_completion_set_property;
190   object_class->get_property = gtk_entry_completion_get_property;
191   object_class->finalize = gtk_entry_completion_finalize;
192
193   klass->match_selected = gtk_entry_completion_match_selected;
194   klass->insert_prefix = gtk_entry_completion_real_insert_prefix;
195
196   /**
197    * GtkEntryCompletion::insert-prefix:
198    * @widget: the object which received the signal
199    * @prefix: the common prefix of all possible completions
200    * 
201    * Gets emitted when the inline autocompletion is triggered. 
202    * The default behaviour is to make the entry display the 
203    * whole prefix and select the newly inserted part.
204    *
205    * Applications may connect to this signal in order to insert only a
206    * smaller part of the @prefix into the entry - e.g. the entry used in
207    * the #GtkFileChooser inserts only the part of the prefix up to the 
208    * next '/'.
209    *
210    * Return value: %TRUE if the signal has been handled
211    * 
212    * Since: 2.6
213    */ 
214   entry_completion_signals[INSERT_PREFIX] =
215     g_signal_new ("insert_prefix",
216                   G_TYPE_FROM_CLASS (klass),
217                   G_SIGNAL_RUN_LAST,
218                   G_STRUCT_OFFSET (GtkEntryCompletionClass, insert_prefix),
219                   _gtk_boolean_handled_accumulator, NULL,
220                   _gtk_marshal_BOOLEAN__STRING,
221                   G_TYPE_BOOLEAN, 1,
222                   G_TYPE_STRING);
223
224   /**
225    * GtkEntryCompletion::match-selected:
226    * @widget: the object which received the signal
227    * @model: the #GtkTreeModel containing the matches
228    * @iter: a #GtkTreeIter positioned at the selected match
229    * 
230    * Gets emitted when a match from the list is selected. 
231    * The default behaviour is to replace the contents of the 
232    * entry with the contents of the text column in the row 
233    * pointed to by @iter.
234    *
235    * Return value: %TRUE if the signal has been handled
236    * 
237    * Since: 2.4
238    */ 
239   entry_completion_signals[MATCH_SELECTED] =
240     g_signal_new ("match_selected",
241                   G_TYPE_FROM_CLASS (klass),
242                   G_SIGNAL_RUN_LAST,
243                   G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
244                   _gtk_boolean_handled_accumulator, NULL,
245                   _gtk_marshal_BOOLEAN__OBJECT_BOXED,
246                   G_TYPE_BOOLEAN, 2,
247                   GTK_TYPE_TREE_MODEL,
248                   GTK_TYPE_TREE_ITER);
249                   
250   /**
251    * GtkEntryCompletion::action-activated:
252    * @widget: the object which received the signal
253    * @index: the index of the activated action
254    *
255    * Gets emitted when an action is activated.
256    * 
257    * Since: 2.4
258    */
259   entry_completion_signals[ACTION_ACTIVATED] =
260     g_signal_new ("action_activated",
261                   G_TYPE_FROM_CLASS (klass),
262                   G_SIGNAL_RUN_LAST,
263                   G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
264                   NULL, NULL,
265                   _gtk_marshal_NONE__INT,
266                   G_TYPE_NONE, 1,
267                   G_TYPE_INT);
268
269   g_object_class_install_property (object_class,
270                                    PROP_MODEL,
271                                    g_param_spec_object ("model",
272                                                         P_("Completion Model"),
273                                                         P_("The model to find matches in"),
274                                                         GTK_TYPE_TREE_MODEL,
275                                                         G_PARAM_READWRITE));
276   g_object_class_install_property (object_class,
277                                    PROP_MINIMUM_KEY_LENGTH,
278                                    g_param_spec_int ("minimum_key_length",
279                                                      P_("Minimum Key Length"),
280                                                      P_("Minimum length of the search key in order to look up matches"),
281                                                      0,
282                                                      G_MAXINT,
283                                                      1,
284                                                      G_PARAM_READWRITE));
285   /**
286    * GtkEntryCompletion:text-column:
287    *
288    * The column of the model containing the strings.
289    *
290    * Since: 2.6
291    */
292   g_object_class_install_property (object_class,
293                                    PROP_TEXT_COLUMN,
294                                    g_param_spec_int ("text_column",
295                                                      P_("Text column"),
296                                                      P_("The column of the model containing the strings."),
297                                                      -1,
298                                                      G_MAXINT,
299                                                      -1,
300                                                      G_PARAM_READWRITE));
301
302   /**
303    * GtkEntryCompletion:inline-completion:
304    * 
305    * Determines whether the common prefix of the possible completions 
306    * should be inserted automatically in the entry.
307    *
308    * Since: 2.6
309    **/
310   g_object_class_install_property (object_class,
311                                    PROP_INLINE_COMPLETION,
312                                    g_param_spec_boolean ("inline_completion",
313                                                          P_("Inline completion"),
314                                                          P_("Whether the common prefix should be inserted automatically"),
315                                                          FALSE,
316                                                          G_PARAM_READWRITE));
317   /**
318    * GtkEntryCompletion:popup-completion:
319    * 
320    * Determines whether the possible completions should be 
321    * shown in a popup window. 
322    *
323    * Since: 2.6
324    **/
325   g_object_class_install_property (object_class,
326                                    PROP_POPUP_COMPLETION,
327                                    g_param_spec_boolean ("popup_completion",
328                                                          P_("Popup completion"),
329                                                          P_("Whether the completions should be shown in a popup window"),
330                                                          TRUE,
331                                                          G_PARAM_READWRITE));
332
333   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
334 }
335
336 static void
337 gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
338 {
339   iface->pack_start = gtk_entry_completion_pack_start;
340   iface->pack_end = gtk_entry_completion_pack_end;
341   iface->clear = gtk_entry_completion_clear;
342   iface->add_attribute = gtk_entry_completion_add_attribute;
343   iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
344   iface->clear_attributes = gtk_entry_completion_clear_attributes;
345   iface->reorder = gtk_entry_completion_reorder;
346 }
347
348 static void
349 gtk_entry_completion_init (GtkEntryCompletion *completion)
350 {
351   GtkCellRenderer *cell;
352   GtkTreeSelection *sel;
353   GtkEntryCompletionPrivate *priv;
354   GtkWidget *popup_frame;
355
356   /* yes, also priv, need to keep the code readable */
357   priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
358
359   priv->minimum_key_length = 1;
360   priv->text_column = -1;
361   priv->has_completion = FALSE;
362   priv->inline_completion = FALSE;
363   priv->popup_completion = TRUE;
364
365   /* completions */
366   priv->filter_model = NULL;
367
368   priv->tree_view = gtk_tree_view_new ();
369   g_signal_connect (priv->tree_view, "button_press_event",
370                     G_CALLBACK (gtk_entry_completion_list_button_press),
371                     completion);
372   g_signal_connect (priv->tree_view, "enter_notify_event",
373                     G_CALLBACK (gtk_entry_completion_list_enter_notify),
374                     completion);
375   g_signal_connect (priv->tree_view, "motion_notify_event",
376                     G_CALLBACK (gtk_entry_completion_list_motion_notify),
377                     completion);
378   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
379   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
380
381   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
382   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
383   gtk_tree_selection_unselect_all (sel);
384   g_signal_connect (sel, "changed",
385                     G_CALLBACK (gtk_entry_completion_selection_changed),
386                     completion);
387   priv->first_sel_changed = TRUE;
388
389   priv->column = gtk_tree_view_column_new ();
390   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
391
392   priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
393   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
394                                   GTK_POLICY_NEVER,
395                                   GTK_POLICY_AUTOMATIC);
396   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
397                                        GTK_SHADOW_NONE);
398
399   /* a nasty hack to get the completions treeview to size nicely */
400   gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
401
402   /* actions */
403   priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
404
405   priv->action_view =
406     gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
407   g_signal_connect (priv->action_view, "button_press_event",
408                     G_CALLBACK (gtk_entry_completion_action_button_press),
409                     completion);
410   g_signal_connect (priv->action_view, "enter_notify_event",
411                     G_CALLBACK (gtk_entry_completion_list_enter_notify),
412                     completion);
413   g_signal_connect (priv->action_view, "motion_notify_event",
414                     G_CALLBACK (gtk_entry_completion_list_motion_notify),
415                     completion);
416   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
417   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->action_view), TRUE);
418
419   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
420   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
421   gtk_tree_selection_unselect_all (sel);
422
423   cell = gtk_cell_renderer_text_new ();
424   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
425                                               0, "",
426                                               cell,
427                                               gtk_entry_completion_action_data_func,
428                                               NULL,
429                                               NULL);
430
431   /* pack it all */
432   priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
433   gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
434   g_signal_connect (priv->popup_window, "key_press_event",
435                     G_CALLBACK (gtk_entry_completion_popup_key_press),
436                     completion);
437   g_signal_connect (priv->popup_window, "button_press_event",
438                     G_CALLBACK (gtk_entry_completion_popup_button_press),
439                     completion);
440
441   popup_frame = gtk_frame_new (NULL);
442   gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
443                              GTK_SHADOW_ETCHED_IN);
444   gtk_widget_show (popup_frame);
445   gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
446   
447   priv->vbox = gtk_vbox_new (FALSE, 0);
448   gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
449
450   gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
451   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
452                       TRUE, TRUE, 0);
453
454   /* we don't want to see the action treeview when no actions have
455    * been inserted, so we pack the action treeview after the first
456    * action has been added
457    */
458 }
459
460 static void
461 gtk_entry_completion_set_property (GObject      *object,
462                                    guint         prop_id,
463                                    const GValue *value,
464                                    GParamSpec   *pspec)
465 {
466   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
467   GtkEntryCompletionPrivate *priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
468
469   switch (prop_id)
470     {
471       case PROP_MODEL:
472         gtk_entry_completion_set_model (completion,
473                                         g_value_get_object (value));
474         break;
475
476       case PROP_MINIMUM_KEY_LENGTH:
477         gtk_entry_completion_set_minimum_key_length (completion,
478                                                      g_value_get_int (value));
479         break;
480
481       case PROP_TEXT_COLUMN:
482         priv->text_column = g_value_get_int (value);
483         break;
484
485       case PROP_INLINE_COMPLETION:
486         priv->inline_completion = g_value_get_boolean (value);
487         break;
488
489       case PROP_POPUP_COMPLETION:
490         priv->popup_completion = g_value_get_boolean (value);
491         break;
492
493       default:
494         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
495         break;
496     }
497 }
498
499 static void
500 gtk_entry_completion_get_property (GObject    *object,
501                                    guint       prop_id,
502                                    GValue     *value,
503                                    GParamSpec *pspec)
504 {
505   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
506
507   switch (prop_id)
508     {
509       case PROP_MODEL:
510         g_value_set_object (value,
511                             gtk_entry_completion_get_model (completion));
512         break;
513
514       case PROP_MINIMUM_KEY_LENGTH:
515         g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
516         break;
517
518       case PROP_TEXT_COLUMN:
519         g_value_set_int (value, gtk_entry_completion_get_text_column (completion));
520         break;
521
522       case PROP_INLINE_COMPLETION:
523         g_value_set_boolean (value, gtk_entry_completion_get_inline_completion (completion));
524         break;
525
526       case PROP_POPUP_COMPLETION:
527         g_value_set_boolean (value, gtk_entry_completion_get_popup_completion (completion));
528         break;
529
530       default:
531         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
532         break;
533     }
534 }
535
536 static void
537 gtk_entry_completion_finalize (GObject *object)
538 {
539   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
540
541   if (completion->priv->tree_view)
542     gtk_widget_destroy (completion->priv->tree_view);
543
544   if (completion->priv->entry)
545     gtk_entry_set_completion (GTK_ENTRY (completion->priv->entry), NULL);
546
547   if (completion->priv->actions)
548     g_object_unref (completion->priv->actions);
549
550   if (completion->priv->case_normalized_key)
551     g_free (completion->priv->case_normalized_key);
552
553   if (completion->priv->popup_window)
554     gtk_widget_destroy (completion->priv->popup_window);
555
556   G_OBJECT_CLASS (parent_class)->finalize (object);
557 }
558
559 /* implement cell layout interface */
560 static void
561 gtk_entry_completion_pack_start (GtkCellLayout   *cell_layout,
562                                  GtkCellRenderer *cell,
563                                  gboolean         expand)
564 {
565   GtkEntryCompletionPrivate *priv;
566
567   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
568
569   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
570
571   gtk_tree_view_column_pack_start (priv->column, cell, expand);
572 }
573
574 static void
575 gtk_entry_completion_pack_end (GtkCellLayout   *cell_layout,
576                                GtkCellRenderer *cell,
577                                gboolean         expand)
578 {
579   GtkEntryCompletionPrivate *priv;
580
581   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
582
583   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
584
585   gtk_tree_view_column_pack_end (priv->column, cell, expand);
586 }
587
588 static void
589 gtk_entry_completion_clear (GtkCellLayout *cell_layout)
590 {
591   GtkEntryCompletionPrivate *priv;
592
593   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
594
595   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
596
597   gtk_tree_view_column_clear (priv->column);
598 }
599
600 static void
601 gtk_entry_completion_add_attribute (GtkCellLayout   *cell_layout,
602                                     GtkCellRenderer *cell,
603                                     const gchar     *attribute,
604                                     gint             column)
605 {
606   GtkEntryCompletionPrivate *priv;
607
608   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
609
610   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
611
612   gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
613 }
614
615 static void
616 gtk_entry_completion_set_cell_data_func (GtkCellLayout          *cell_layout,
617                                          GtkCellRenderer        *cell,
618                                          GtkCellLayoutDataFunc   func,
619                                          gpointer                func_data,
620                                          GDestroyNotify          destroy)
621 {
622   GtkEntryCompletionPrivate *priv;
623
624   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
625
626   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
627
628   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
629                                       cell, func, func_data, destroy);
630 }
631
632 static void
633 gtk_entry_completion_clear_attributes (GtkCellLayout   *cell_layout,
634                                        GtkCellRenderer *cell)
635 {
636   GtkEntryCompletionPrivate *priv;
637
638   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
639
640   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
641
642   gtk_tree_view_column_clear_attributes (priv->column, cell);
643 }
644
645 static void
646 gtk_entry_completion_reorder (GtkCellLayout   *cell_layout,
647                               GtkCellRenderer *cell,
648                               gint             position)
649 {
650   GtkEntryCompletionPrivate *priv;
651
652   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
653
654   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
655
656   gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
657 }
658
659 /* all those callbacks */
660 static gboolean
661 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
662                                               const gchar        *key,
663                                               GtkTreeIter        *iter,
664                                               gpointer            user_data)
665 {
666   gchar *item = NULL;
667   gchar *normalized_string;
668   gchar *case_normalized_string;
669
670   gboolean ret = FALSE;
671
672   GtkTreeModel *model;
673
674   model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
675
676   g_return_val_if_fail (gtk_tree_model_get_column_type (model, completion->priv->text_column) == G_TYPE_STRING, 
677                         FALSE);
678
679   gtk_tree_model_get (model, iter,
680                       completion->priv->text_column, &item,
681                       -1);
682
683   if (item != NULL)
684     {
685       normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
686       case_normalized_string = g_utf8_casefold (normalized_string, -1);
687       
688       if (!strncmp (key, case_normalized_string, strlen (key)))
689         ret = TRUE;
690       
691       g_free (item);
692       g_free (normalized_string);
693       g_free (case_normalized_string);
694     }
695
696   return ret;
697 }
698
699 static gboolean
700 gtk_entry_completion_visible_func (GtkTreeModel *model,
701                                    GtkTreeIter  *iter,
702                                    gpointer      data)
703 {
704   gboolean ret = FALSE;
705
706   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
707
708   if (!completion->priv->case_normalized_key)
709     return ret;
710
711   if (completion->priv->match_func)
712     ret = (* completion->priv->match_func) (completion,
713                                             completion->priv->case_normalized_key,
714                                             iter,
715                                             completion->priv->match_data);
716   else if (completion->priv->text_column >= 0)
717     ret = gtk_entry_completion_default_completion_func (completion,
718                                                         completion->priv->case_normalized_key,
719                                                         iter,
720                                                         NULL);
721
722   return ret;
723 }
724
725 static gboolean
726 gtk_entry_completion_popup_key_press (GtkWidget   *widget,
727                                       GdkEventKey *event,
728                                       gpointer     user_data)
729 {
730   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
731
732   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
733     return FALSE;
734
735   /* propagate event to the entry */
736   gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
737
738   return TRUE;
739 }
740
741 static gboolean
742 gtk_entry_completion_popup_button_press (GtkWidget      *widget,
743                                          GdkEventButton *event,
744                                          gpointer        user_data)
745 {
746   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
747
748   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
749     return FALSE;
750
751   /* if we come here, it's usually time to popdown */
752   _gtk_entry_completion_popdown (completion);
753
754   return TRUE;
755 }
756
757 static gboolean
758 gtk_entry_completion_list_button_press (GtkWidget      *widget,
759                                         GdkEventButton *event,
760                                         gpointer        user_data)
761 {
762   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
763   GtkTreePath *path = NULL;
764
765   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
766     return FALSE;
767
768   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
769                                      event->x, event->y,
770                                      &path, NULL, NULL, NULL))
771     {
772       GtkTreeIter iter;
773       gboolean entry_set;
774
775       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
776                                &iter, path);
777       gtk_tree_path_free (path);
778
779       g_signal_handler_block (completion->priv->entry,
780                               completion->priv->changed_id);
781       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
782                      0, GTK_TREE_MODEL (completion->priv->filter_model),
783                      &iter, &entry_set);
784       g_signal_handler_unblock (completion->priv->entry,
785                                 completion->priv->changed_id);
786
787       _gtk_entry_completion_popdown (completion);
788
789       return TRUE;
790     }
791
792   return FALSE;
793 }
794
795 static gboolean
796 gtk_entry_completion_action_button_press (GtkWidget      *widget,
797                                           GdkEventButton *event,
798                                           gpointer        user_data)
799 {
800   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
801   GtkTreePath *path = NULL;
802
803   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
804     return FALSE;
805
806   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
807                                      event->x, event->y,
808                                      &path, NULL, NULL, NULL))
809     {
810       g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
811                      0, gtk_tree_path_get_indices (path)[0]);
812       gtk_tree_path_free (path);
813
814       _gtk_entry_completion_popdown (completion);
815       return TRUE;
816     }
817
818   return FALSE;
819 }
820
821 static void
822 gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
823                                        GtkCellRenderer   *cell,
824                                        GtkTreeModel      *model,
825                                        GtkTreeIter       *iter,
826                                        gpointer           data)
827 {
828   gchar *string = NULL;
829   gboolean markup;
830
831   gtk_tree_model_get (model, iter,
832                       0, &string,
833                       1, &markup,
834                       -1);
835
836   if (!string)
837     return;
838
839   if (markup)
840     g_object_set (cell,
841                   "text", NULL,
842                   "markup", string,
843                   NULL);
844   else
845     g_object_set (cell,
846                   "markup", NULL,
847                   "text", string,
848                   NULL);
849
850   g_free (string);
851 }
852
853 static void
854 gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
855                                         gpointer          data)
856 {
857   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
858
859   if (completion->priv->first_sel_changed)
860     {
861       completion->priv->first_sel_changed = FALSE;
862       if (gtk_widget_is_focus (completion->priv->tree_view))
863         gtk_tree_selection_unselect_all (selection);
864     }
865 }
866
867 /* public API */
868
869 /**
870  * gtk_entry_completion_new:
871  *
872  * Creates a new #GtkEntryCompletion object.
873  *
874  * Return value: A newly created #GtkEntryCompletion object.
875  *
876  * Since: 2.4
877  */
878 GtkEntryCompletion *
879 gtk_entry_completion_new (void)
880 {
881   GtkEntryCompletion *completion;
882
883   completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
884
885   return completion;
886 }
887
888 /**
889  * gtk_entry_completion_get_entry:
890  * @completion: A #GtkEntryCompletion.
891  *
892  * Gets the entry @completion has been attached to.
893  *
894  * Return value: The entry @completion has been attached to.
895  *
896  * Since: 2.4
897  */
898 GtkWidget *
899 gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
900 {
901   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
902
903   return completion->priv->entry;
904 }
905
906 /**
907  * gtk_entry_completion_set_model:
908  * @completion: A #GtkEntryCompletion.
909  * @model: The #GtkTreeModel.
910  *
911  * Sets the model for a #GtkEntryCompletion. If @completion already has
912  * a model set, it will remove it before setting the new model.
913  * If model is %NULL, then it will unset the model.
914  *
915  * Since: 2.4
916  */
917 void
918 gtk_entry_completion_set_model (GtkEntryCompletion *completion,
919                                 GtkTreeModel       *model)
920 {
921   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
922   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
923
924   if (!model)
925     {
926       gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
927                                NULL);
928       _gtk_entry_completion_popdown (completion);
929       completion->priv->filter_model = NULL;
930       return;
931     }
932      
933   /* code will unref the old filter model (if any) */
934   completion->priv->filter_model =
935     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
936   gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
937                                           gtk_entry_completion_visible_func,
938                                           completion,
939                                           NULL);
940
941   gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
942                            GTK_TREE_MODEL (completion->priv->filter_model));
943   g_object_unref (completion->priv->filter_model);
944
945   g_object_notify (G_OBJECT (completion), "model");
946
947   if (GTK_WIDGET_VISIBLE (completion->priv->popup_window))
948     _gtk_entry_completion_resize_popup (completion);
949 }
950
951 /**
952  * gtk_entry_completion_get_model:
953  * @completion: A #GtkEntryCompletion.
954  *
955  * Returns the model the #GtkEntryCompletion is using as data source.
956  * Returns %NULL if the model is unset.
957  *
958  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
959  *
960  * Since: 2.4
961  */
962 GtkTreeModel *
963 gtk_entry_completion_get_model (GtkEntryCompletion *completion)
964 {
965   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
966
967   if (!completion->priv->filter_model)
968     return NULL;
969   
970   return gtk_tree_model_filter_get_model (completion->priv->filter_model);
971 }
972
973 /**
974  * gtk_entry_completion_set_match_func:
975  * @completion: A #GtkEntryCompletion.
976  * @func: The #GtkEntryCompletionMatchFunc to use.
977  * @func_data: The user data for @func.
978  * @func_notify: Destroy notifier for @func_data.
979  *
980  * Sets the match function for @completion to be @func. The match function
981  * is used to determine if a row should or should not be in the completion
982  * list.
983  *
984  * Since: 2.4.
985  */
986 void
987 gtk_entry_completion_set_match_func (GtkEntryCompletion          *completion,
988                                      GtkEntryCompletionMatchFunc  func,
989                                      gpointer                     func_data,
990                                      GDestroyNotify               func_notify)
991 {
992   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
993
994   if (completion->priv->match_notify)
995     (* completion->priv->match_notify) (completion->priv->match_data);
996
997   completion->priv->match_func = func;
998   completion->priv->match_data = func_data;
999   completion->priv->match_notify = func_notify;
1000 }
1001
1002 /**
1003  * gtk_entry_completion_set_minimum_key_length:
1004  * @completion: A #GtkEntryCompletion.
1005  * @length: The minimum length of the key in order to start completing.
1006  *
1007  * Requires the length of the search key for @completion to be at least
1008  * @length. This is useful for long lists, where completing using a small
1009  * key takes a lot of time and will come up with meaningless results anyway
1010  * (ie, a too large dataset).
1011  *
1012  * Since: 2.4
1013  */
1014 void
1015 gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
1016                                              gint                length)
1017 {
1018   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1019   g_return_if_fail (length >= 0);
1020
1021   if (completion->priv->minimum_key_length != length)
1022     {
1023       completion->priv->minimum_key_length = length;
1024      
1025       g_object_notify (G_OBJECT (completion), "minimum_key_length");
1026     }
1027 }
1028
1029 /**
1030  * gtk_entry_completion_get_minimum_key_length:
1031  * @completion: A #GtkEntryCompletion.
1032  *
1033  * Returns the minimum key length as set for @completion.
1034  *
1035  * Return value: The currently used minimum key length.
1036  *
1037  * Since: 2.4
1038  */
1039 gint
1040 gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
1041 {
1042   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
1043
1044   return completion->priv->minimum_key_length;
1045 }
1046
1047 /**
1048  * gtk_entry_completion_complete:
1049  * @completion: A #GtkEntryCompletion.
1050  *
1051  * Requests a completion operation, or in other words a refiltering of the
1052  * current list with completions, using the current key. The completion list
1053  * view will be updated accordingly.
1054  *
1055  * Since: 2.4
1056  */
1057 void
1058 gtk_entry_completion_complete (GtkEntryCompletion *completion)
1059 {
1060   gchar *tmp;
1061
1062   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1063
1064   if (!completion->priv->filter_model)
1065     return;
1066   
1067   if (completion->priv->case_normalized_key)
1068     g_free (completion->priv->case_normalized_key);
1069
1070   tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
1071                           -1, G_NORMALIZE_ALL);
1072   completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
1073   g_free (tmp);
1074
1075   gtk_tree_model_filter_refilter (completion->priv->filter_model);
1076 }
1077
1078 static void
1079 gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
1080                                     gint                index,
1081                                     const gchar        *string,
1082                                     gboolean            markup)
1083 {
1084   GtkTreeIter iter;
1085
1086   gtk_list_store_insert (completion->priv->actions, &iter, index);
1087   gtk_list_store_set (completion->priv->actions, &iter,
1088                       0, string,
1089                       1, markup,
1090                       -1);
1091
1092   if (!completion->priv->action_view->parent)
1093     {
1094       GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
1095
1096       gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
1097                                 path, NULL, FALSE);
1098       gtk_tree_path_free (path);
1099
1100       gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
1101                           completion->priv->action_view, FALSE, FALSE, 0);
1102       gtk_widget_show (completion->priv->action_view);
1103     }
1104 }
1105
1106 /**
1107  * gtk_entry_completion_insert_action_text:
1108  * @completion: A #GtkEntryCompletion.
1109  * @index_: The index of the item to insert.
1110  * @text: Text of the item to insert.
1111  *
1112  * Inserts an action in @completion's action item list at position @index_
1113  * with text @text. If you want the action item to have markup, use
1114  * gtk_entry_completion_insert_action_markup().
1115  *
1116  * Since: 2.4
1117  */
1118 void
1119 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
1120                                          gint                index_,
1121                                          const gchar        *text)
1122 {
1123   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1124   g_return_if_fail (text != NULL);
1125
1126   gtk_entry_completion_insert_action (completion, index_, text, FALSE);
1127 }
1128
1129 /**
1130  * gtk_entry_completion_insert_action_markup:
1131  * @completion: A #GtkEntryCompletion.
1132  * @index_: The index of the item to insert.
1133  * @markup: Markup of the item to insert.
1134  *
1135  * Inserts an action in @completion's action item list at position @index_
1136  * with markup @markup.
1137  *
1138  * Since: 2.4
1139  */
1140 void
1141 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
1142                                            gint                index_,
1143                                            const gchar        *markup)
1144 {
1145   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1146   g_return_if_fail (markup != NULL);
1147
1148   gtk_entry_completion_insert_action (completion, index_, markup, TRUE);
1149 }
1150
1151 /**
1152  * gtk_entry_completion_delete_action:
1153  * @completion: A #GtkEntryCompletion.
1154  * @index_: The index of the item to Delete.
1155  *
1156  * Deletes the action at @index_ from @completion's action list.
1157  *
1158  * Since: 2.4
1159  */
1160 void
1161 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
1162                                     gint                index_)
1163 {
1164   GtkTreeIter iter;
1165
1166   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1167   g_return_if_fail (index_ >= 0);
1168
1169   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
1170                                  &iter, NULL, index_);
1171   gtk_list_store_remove (completion->priv->actions, &iter);
1172 }
1173
1174 /**
1175  * gtk_entry_completion_set_text_column:
1176  * @completion: A #GtkEntryCompletion.
1177  * @column: The column in the model of @completion to get strings from.
1178  *
1179  * Convenience function for setting up the most used case of this code: a
1180  * completion list with just strings. This function will set up @completion
1181  * to have a list displaying all (and just) strings in the completion list,
1182  * and to get those strings from @column in the model of @completion.
1183  *
1184  * This functions creates and adds a #GtkCellRendererText for the selected 
1185  * column. If you need to set the text column, but don't want the cell 
1186  * renderer, use g_object_set() to set the ::text_column property directly.
1187  * 
1188  * Since: 2.4
1189  */
1190 void
1191 gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
1192                                       gint                column)
1193 {
1194   GtkCellRenderer *cell;
1195
1196   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1197   g_return_if_fail (column >= 0);
1198
1199   completion->priv->text_column = column;
1200
1201   cell = gtk_cell_renderer_text_new ();
1202   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1203                               cell, TRUE);
1204   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1205                                  cell,
1206                                  "text", column);
1207
1208   g_object_notify (G_OBJECT (completion), "text_column");
1209 }
1210
1211 /**
1212  * gtk_entry_completion_get_text_column:
1213  * @completion: a #GtkEntryCompletion
1214  * 
1215  * Returns the column in the model of @completion to get strings from.
1216  * 
1217  * Return value: the column containing the strings
1218  *
1219  * Since: 2.6
1220  **/
1221 gint
1222 gtk_entry_completion_get_text_column (GtkEntryCompletion *completion)
1223 {
1224   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), -1);
1225
1226   return completion->priv->text_column;  
1227 }
1228
1229 /* private */
1230
1231 static gboolean
1232 gtk_entry_completion_list_enter_notify (GtkWidget        *widget,
1233                                         GdkEventCrossing *event,
1234                                         gpointer          data)
1235 {
1236   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1237   
1238   return completion->priv->ignore_enter;
1239 }
1240
1241 static gboolean
1242 gtk_entry_completion_list_motion_notify (GtkWidget      *widget,
1243                                          GdkEventMotion *event,
1244                                          gpointer        data)
1245 {
1246   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1247
1248   completion->priv->ignore_enter = FALSE; 
1249   
1250   return FALSE;
1251 }
1252
1253
1254 /* some nasty size requisition */
1255 gboolean
1256 _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
1257 {
1258   gint x, y;
1259   gint matches, items, height, x_border, y_border;
1260   GdkScreen *screen;
1261   gint monitor_num;
1262   GdkRectangle monitor;
1263   GtkRequisition popup_req;
1264   GtkRequisition entry_req;
1265   GtkTreePath *path;
1266   gboolean above;
1267   gint width;
1268
1269   if (!completion->priv->entry->window)
1270     return FALSE;
1271
1272   gdk_window_get_origin (completion->priv->entry->window, &x, &y);
1273   _gtk_entry_get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
1274
1275   matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
1276
1277   items = MIN (matches, 15);
1278
1279   gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
1280                                       NULL, NULL, NULL, &height);
1281
1282   if (items <= 0)
1283     gtk_widget_hide (completion->priv->scrolled_window);
1284   else
1285     gtk_widget_show (completion->priv->scrolled_window);
1286
1287   screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
1288   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1289                                                   GTK_WIDGET (completion->priv->entry)->window);
1290   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1291
1292   width = MIN (completion->priv->entry->allocation.width, monitor.width) - 2 * x_border;
1293   gtk_widget_set_size_request (completion->priv->tree_view, width, items * height);
1294
1295   /* default on no match */
1296   completion->priv->current_selected = -1;
1297
1298   items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
1299
1300   if (items)
1301     {
1302       gtk_widget_show (completion->priv->action_view);
1303       gtk_widget_set_size_request (completion->priv->action_view, width, -1);
1304     }
1305   else
1306     gtk_widget_hide (completion->priv->action_view);
1307
1308   gtk_widget_size_request (completion->priv->popup_window, &popup_req);
1309   gtk_widget_size_request (completion->priv->entry, &entry_req);
1310   
1311   if (x < monitor.x)
1312     x = monitor.x;
1313   else if (x + popup_req.width > monitor.x + monitor.width)
1314     x = monitor.x + monitor.width - popup_req.width;
1315   
1316   if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height)
1317     {
1318       y += entry_req.height;
1319       above = FALSE;
1320     }
1321   else
1322     {
1323       y -= popup_req.height;
1324       above = TRUE;
1325     }
1326   
1327   if (matches > 0) 
1328     {
1329       path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
1330       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path, 
1331                                     NULL, FALSE, 0.0, 0.0);
1332       gtk_tree_path_free (path);
1333     }
1334
1335   gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
1336
1337   return above;
1338 }
1339
1340 void
1341 _gtk_entry_completion_popup (GtkEntryCompletion *completion)
1342 {
1343   GtkTreeViewColumn *column;
1344   GList *renderers;
1345
1346   if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
1347     return;
1348
1349   if (!GTK_WIDGET_MAPPED (completion->priv->entry))
1350     return;
1351
1352   completion->priv->ignore_enter = TRUE;
1353     
1354   column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1355   renderers = gtk_tree_view_column_get_cell_renderers (column);
1356   gtk_widget_ensure_style (completion->priv->tree_view);
1357   g_object_set (GTK_CELL_RENDERER (renderers->data), "cell_background_gdk",
1358                 &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
1359                 NULL);
1360   g_list_free (renderers);
1361
1362   gtk_widget_show_all (completion->priv->vbox);
1363
1364   _gtk_entry_completion_resize_popup (completion);
1365
1366   gtk_widget_show (completion->priv->popup_window);
1367     
1368   gtk_grab_add (completion->priv->popup_window);
1369   gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
1370                     GDK_BUTTON_PRESS_MASK |
1371                     GDK_BUTTON_RELEASE_MASK |
1372                     GDK_POINTER_MOTION_MASK,
1373                     NULL, NULL, GDK_CURRENT_TIME);
1374 }
1375
1376 void
1377 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
1378 {
1379   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
1380     return;
1381
1382   completion->priv->ignore_enter = FALSE;
1383   
1384   gdk_pointer_ungrab (GDK_CURRENT_TIME);
1385   gtk_grab_remove (completion->priv->popup_window);
1386
1387   gtk_widget_hide (completion->priv->popup_window);
1388 }
1389
1390 static gboolean 
1391 gtk_entry_completion_match_selected (GtkEntryCompletion *completion,
1392                                      GtkTreeModel       *model,
1393                                      GtkTreeIter        *iter)
1394 {
1395   gchar *str = NULL;
1396
1397   gtk_tree_model_get (model, iter, completion->priv->text_column, &str, -1);
1398   gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
1399   
1400   /* move cursor to the end */
1401   gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry), -1);
1402   
1403   g_free (str);
1404
1405   return TRUE;
1406 }
1407
1408
1409 static gchar *
1410 gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion)
1411 {
1412   GtkTreeIter iter;
1413   gchar *prefix = NULL;
1414   gboolean valid;
1415
1416   const gchar *key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
1417
1418   valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (completion->priv->filter_model),
1419                                          &iter);
1420   
1421   while (valid)
1422     {
1423       gchar *text;
1424       
1425       gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
1426                           &iter, completion->priv->text_column, &text,
1427                           -1);
1428
1429       if (g_str_has_prefix (text, key))
1430         {
1431           if (!prefix)
1432             prefix = g_strdup (text);
1433           else
1434             {
1435               gchar *p = prefix;
1436               const gchar *q = text;
1437               
1438               while (*p && *p == *q)
1439                 {
1440                   p++;
1441                   q++;
1442                 }
1443               
1444               *p = '\0';
1445             }
1446         }
1447       
1448       g_free (text);
1449       valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (completion->priv->filter_model),
1450                                         &iter);
1451     }
1452
1453   return prefix;
1454 }
1455
1456
1457 static gboolean
1458 gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
1459                                          const gchar        *prefix)
1460 {
1461   if (prefix)
1462     {
1463       gint key_len;
1464       gint prefix_len;
1465       gint pos;
1466       const gchar *key;
1467
1468       prefix_len = g_utf8_strlen (prefix, -1);
1469
1470       key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
1471       key_len = g_utf8_strlen (key, -1);
1472
1473       if (prefix_len > key_len)
1474         {
1475           gtk_editable_insert_text (GTK_EDITABLE (completion->priv->entry),
1476                                     prefix + key_len, -1, &pos);
1477           gtk_editable_select_region (GTK_EDITABLE (completion->priv->entry),
1478                                       key_len, prefix_len);
1479
1480           completion->priv->has_completion = TRUE;
1481         }
1482     }
1483
1484   return TRUE;
1485 }
1486
1487 /**
1488  * gtk_entry_completion_insert_prefix:
1489  * @completion: a #GtkEntryCompletion
1490  * 
1491  * Requests a prefix insertion. 
1492  * 
1493  * Since: 2.6
1494  **/
1495 void
1496 gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
1497 {
1498   gboolean done;
1499   gchar *prefix;
1500
1501   g_signal_handler_block (completion->priv->entry,
1502                           completion->priv->insert_text_id);
1503   prefix = gtk_entry_completion_compute_prefix (completion);
1504   if (prefix)
1505     {
1506       g_signal_emit (completion, entry_completion_signals[INSERT_PREFIX],
1507                      0, prefix, &done);
1508       g_free (prefix);
1509     }
1510   g_signal_handler_unblock (completion->priv->entry,
1511                             completion->priv->insert_text_id);
1512 }
1513
1514 /**
1515  * gtk_entry_completion_set_inline_completion:
1516  * @completion: a #GtkEntryCompletion
1517  * @inline_completion: %TRUE to do inline completion
1518  * 
1519  * Sets whether the common prefix of the possible completions should
1520  * be automatically inserted in the entry.
1521  * 
1522  * Since: 2.6
1523  **/
1524 void 
1525 gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
1526                                             gboolean            inline_completion)
1527 {
1528   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1529   
1530   inline_completion = inline_completion != FALSE;
1531
1532   if (completion->priv->inline_completion != inline_completion)
1533     {
1534       completion->priv->inline_completion = inline_completion;
1535
1536       g_object_notify (G_OBJECT (completion), "inline_completion");
1537     }
1538 }
1539
1540
1541 /**
1542  * gtk_entry_completion_get_inline_completion:
1543  * @completion: a #GtkEntryCompletion
1544  * 
1545  * Returns whether the common prefix of the possible completions should
1546  * be automatically inserted in the entry.
1547  * 
1548  * Return value: %TRUE if inline completion is turned on
1549  * 
1550  * Since: 2.6
1551  **/
1552 gboolean
1553 gtk_entry_completion_get_inline_completion (GtkEntryCompletion *completion)
1554 {
1555   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1556   
1557   return completion->priv->inline_completion;
1558 }
1559
1560 /**
1561  * gtk_entry_completion_set_popup_completion:
1562  * @completion: a #GtkEntryCompletion
1563  * @popup_completion: %TRUE to do popup completion
1564  * 
1565  * Sets whether the completions should be presented in a popup window.
1566  * 
1567  * Since: 2.6
1568  **/
1569 void 
1570 gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion,
1571                                            gboolean            popup_completion)
1572 {
1573   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1574   
1575   popup_completion = popup_completion != FALSE;
1576
1577   if (completion->priv->popup_completion != popup_completion)
1578     {
1579       completion->priv->popup_completion = popup_completion;
1580
1581       g_object_notify (G_OBJECT (completion), "popup_completion");
1582     }
1583 }
1584
1585
1586 /**
1587  * gtk_entry_completion_get_popup_completion:
1588  * @completion: a #GtkEntryCompletion
1589  * 
1590  * Returns whether the completions should be presented in a popup window.
1591  * 
1592  * Return value: %TRUE if popup completion is turned on
1593  * 
1594  * Since: 2.6
1595  **/
1596 gboolean
1597 gtk_entry_completion_get_popup_completion (GtkEntryCompletion *completion)
1598 {
1599   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1600   
1601   return completion->priv->popup_completion;
1602 }