]> Pileus Git - ~andy/gtk/blob - gtk/gtkentrycompletion.c
e3dd01fdad82a4d4df26ce5772e80ff1b74563f5
[~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 "gtkentrycompletion.h"
22 #include "gtkentryprivate.h"
23 #include "gtkcelllayout.h"
24
25 #include "gtkintl.h"
26 #include "gtkcellrenderertext.h"
27 #include "gtkframe.h"
28 #include "gtktreeselection.h"
29 #include "gtktreeview.h"
30 #include "gtkscrolledwindow.h"
31 #include "gtkvbox.h"
32 #include "gtkwindow.h"
33 #include "gtkentry.h"
34 #include "gtkmain.h"
35 #include "gtksignal.h"
36 #include "gtkmarshalers.h"
37
38 #include "gtkprivate.h"
39 #include "gtkalias.h"
40
41 #include <string.h>
42
43
44 /* signals */
45 enum
46 {
47   INSERT_PREFIX,
48   MATCH_SELECTED,
49   ACTION_ACTIVATED,
50   CURSOR_ON_MATCH,
51   LAST_SIGNAL
52 };
53
54 /* properties */
55 enum
56 {
57   PROP_0,
58   PROP_MODEL,
59   PROP_MINIMUM_KEY_LENGTH,
60   PROP_TEXT_COLUMN,
61   PROP_INLINE_COMPLETION,
62   PROP_POPUP_COMPLETION,
63   PROP_POPUP_SET_WIDTH,
64   PROP_POPUP_SINGLE_MATCH,
65   PROP_INLINE_SELECTION
66 };
67
68 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
69
70 static void                                                             gtk_entry_completion_cell_layout_init    (GtkCellLayoutIface      *iface);
71 static void     gtk_entry_completion_set_property        (GObject      *object,
72                                                           guint         prop_id,
73                                                           const GValue *value,
74                                                           GParamSpec   *pspec);
75 static void     gtk_entry_completion_get_property        (GObject      *object,
76                                                           guint         prop_id,
77                                                           GValue       *value,
78                                                           GParamSpec   *pspec);
79 static void                                                             gtk_entry_completion_finalize            (GObject                 *object);
80
81 static void     gtk_entry_completion_pack_start          (GtkCellLayout         *cell_layout,
82                                                           GtkCellRenderer       *cell,
83                                                           gboolean               expand);
84 static void     gtk_entry_completion_pack_end            (GtkCellLayout         *cell_layout,
85                                                           GtkCellRenderer       *cell,
86                                                           gboolean               expand);
87 static void                                                                      gtk_entry_completion_clear               (GtkCellLayout           *cell_layout);
88 static void     gtk_entry_completion_add_attribute       (GtkCellLayout         *cell_layout,
89                                                           GtkCellRenderer       *cell,
90                                                           const char            *attribute,
91                                                           gint                   column);
92 static void     gtk_entry_completion_set_cell_data_func  (GtkCellLayout         *cell_layout,
93                                                           GtkCellRenderer       *cell,
94                                                           GtkCellLayoutDataFunc  func,
95                                                           gpointer               func_data,
96                                                           GDestroyNotify         destroy);
97 static void     gtk_entry_completion_clear_attributes    (GtkCellLayout         *cell_layout,
98                                                           GtkCellRenderer       *cell);
99 static void     gtk_entry_completion_reorder             (GtkCellLayout         *cell_layout,
100                                                           GtkCellRenderer       *cell,
101                                                           gint                   position);
102
103 static gboolean gtk_entry_completion_visible_func        (GtkTreeModel       *model,
104                                                           GtkTreeIter        *iter,
105                                                           gpointer            data);
106 static gboolean gtk_entry_completion_popup_key_event     (GtkWidget          *widget,
107                                                           GdkEventKey        *event,
108                                                           gpointer            user_data);
109 static gboolean gtk_entry_completion_popup_button_press  (GtkWidget          *widget,
110                                                           GdkEventButton     *event,
111                                                           gpointer            user_data);
112 static gboolean gtk_entry_completion_list_button_press   (GtkWidget          *widget,
113                                                           GdkEventButton     *event,
114                                                           gpointer            user_data);
115 static gboolean gtk_entry_completion_action_button_press (GtkWidget          *widget,
116                                                           GdkEventButton     *event,
117                                                           gpointer            user_data);
118 static void     gtk_entry_completion_selection_changed   (GtkTreeSelection   *selection,
119                                                           gpointer            data);
120 static gboolean gtk_entry_completion_list_enter_notify   (GtkWidget          *widget,
121                                                           GdkEventCrossing   *event,
122                                                           gpointer            data);
123 static gboolean gtk_entry_completion_list_motion_notify  (GtkWidget          *widget,
124                                                           GdkEventMotion     *event,
125                                                           gpointer            data);
126 static void     gtk_entry_completion_insert_action       (GtkEntryCompletion *completion,
127                                                           gint                index,
128                                                           const gchar        *string,
129                                                           gboolean            markup);
130 static void     gtk_entry_completion_action_data_func    (GtkTreeViewColumn  *tree_column,
131                                                           GtkCellRenderer    *cell,
132                                                           GtkTreeModel       *model,
133                                                           GtkTreeIter        *iter,
134                                                           gpointer            data);
135
136 static gboolean gtk_entry_completion_match_selected      (GtkEntryCompletion *completion,
137                                                           GtkTreeModel       *model,
138                                                           GtkTreeIter        *iter);
139 static gboolean gtk_entry_completion_real_insert_prefix  (GtkEntryCompletion *completion,
140                                                           const gchar        *prefix);
141 static gboolean gtk_entry_completion_cursor_on_match     (GtkEntryCompletion *completion,
142                                                           GtkTreeModel       *model,
143                                                           GtkTreeIter        *iter);
144 static gboolean gtk_entry_completion_insert_completion   (GtkEntryCompletion *completion,
145                                                           GtkTreeModel       *model,
146                                                           GtkTreeIter        *iter);
147 static void     gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
148                                                              const gchar *text);
149
150 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
151
152 /* GtkBuildable */
153 static void     gtk_entry_completion_buildable_init      (GtkBuildableIface  *iface);
154
155 G_DEFINE_TYPE_WITH_CODE (GtkEntryCompletion, gtk_entry_completion, G_TYPE_OBJECT,
156                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
157                                                 gtk_entry_completion_cell_layout_init)
158                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
159                                                 gtk_entry_completion_buildable_init))
160
161
162 static void
163 gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
164 {
165   GObjectClass *object_class;
166
167   object_class = (GObjectClass *)klass;
168
169   object_class->set_property = gtk_entry_completion_set_property;
170   object_class->get_property = gtk_entry_completion_get_property;
171   object_class->finalize = gtk_entry_completion_finalize;
172
173   klass->match_selected = gtk_entry_completion_match_selected;
174   klass->insert_prefix = gtk_entry_completion_real_insert_prefix;
175   klass->cursor_on_match = gtk_entry_completion_cursor_on_match;
176
177   /**
178    * GtkEntryCompletion::insert-prefix:
179    * @widget: the object which received the signal
180    * @prefix: the common prefix of all possible completions
181    * 
182    * Gets emitted when the inline autocompletion is triggered. 
183    * The default behaviour is to make the entry display the 
184    * whole prefix and select the newly inserted part.
185    *
186    * Applications may connect to this signal in order to insert only a
187    * smaller part of the @prefix into the entry - e.g. the entry used in
188    * the #GtkFileChooser inserts only the part of the prefix up to the 
189    * next '/'.
190    *
191    * Return value: %TRUE if the signal has been handled
192    * 
193    * Since: 2.6
194    */ 
195   entry_completion_signals[INSERT_PREFIX] =
196     g_signal_new (I_("insert_prefix"),
197                   G_TYPE_FROM_CLASS (klass),
198                   G_SIGNAL_RUN_LAST,
199                   G_STRUCT_OFFSET (GtkEntryCompletionClass, insert_prefix),
200                   _gtk_boolean_handled_accumulator, NULL,
201                   _gtk_marshal_BOOLEAN__STRING,
202                   G_TYPE_BOOLEAN, 1,
203                   G_TYPE_STRING);
204
205   /**
206    * GtkEntryCompletion::match-selected:
207    * @widget: the object which received the signal
208    * @model: the #GtkTreeModel containing the matches
209    * @iter: a #GtkTreeIter positioned at the selected match
210    * 
211    * Gets emitted when a match from the list is selected. 
212    * The default behaviour is to replace the contents of the 
213    * entry with the contents of the text column in the row 
214    * pointed to by @iter.
215    *
216    * Return value: %TRUE if the signal has been handled
217    * 
218    * Since: 2.4
219    */ 
220   entry_completion_signals[MATCH_SELECTED] =
221     g_signal_new (I_("match_selected"),
222                   G_TYPE_FROM_CLASS (klass),
223                   G_SIGNAL_RUN_LAST,
224                   G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
225                   _gtk_boolean_handled_accumulator, NULL,
226                   _gtk_marshal_BOOLEAN__OBJECT_BOXED,
227                   G_TYPE_BOOLEAN, 2,
228                   GTK_TYPE_TREE_MODEL,
229                   GTK_TYPE_TREE_ITER);
230   /**
231    * GtkEntryCompletion::cursor-on-match:
232    * @widget: the object which received the signal
233    * @model: the #GtkTreeModel containing the matches
234    * @iter: a #GtkTreeIter positioned at the selected match
235    * 
236    * Gets emitted when a match from the cursor is on a match
237    * of the list.The default behaviour is to replace the contents
238    * of the entry with the contents of the text column in the row 
239    * pointed to by @iter.
240    *
241    * Return value: %TRUE if the signal has been handled
242    * 
243    * Since: 2.12
244    */ 
245
246   entry_completion_signals[CURSOR_ON_MATCH] =
247     g_signal_new (I_("cursor_on_match"),
248                   G_TYPE_FROM_CLASS (klass),
249                   G_SIGNAL_RUN_LAST,
250                   G_STRUCT_OFFSET (GtkEntryCompletionClass, cursor_on_match),
251                   _gtk_boolean_handled_accumulator, NULL,
252                   _gtk_marshal_BOOLEAN__OBJECT_BOXED,
253                   G_TYPE_BOOLEAN, 2,
254                   GTK_TYPE_TREE_MODEL,
255                   GTK_TYPE_TREE_ITER);
256                   
257   /**
258    * GtkEntryCompletion::action-activated:
259    * @widget: the object which received the signal
260    * @index: the index of the activated action
261    *
262    * Gets emitted when an action is activated.
263    * 
264    * Since: 2.4
265    */
266   entry_completion_signals[ACTION_ACTIVATED] =
267     g_signal_new (I_("action_activated"),
268                   G_TYPE_FROM_CLASS (klass),
269                   G_SIGNAL_RUN_LAST,
270                   G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
271                   NULL, NULL,
272                   _gtk_marshal_NONE__INT,
273                   G_TYPE_NONE, 1,
274                   G_TYPE_INT);
275
276   g_object_class_install_property (object_class,
277                                    PROP_MODEL,
278                                    g_param_spec_object ("model",
279                                                         P_("Completion Model"),
280                                                         P_("The model to find matches in"),
281                                                         GTK_TYPE_TREE_MODEL,
282                                                         GTK_PARAM_READWRITE));
283   g_object_class_install_property (object_class,
284                                    PROP_MINIMUM_KEY_LENGTH,
285                                    g_param_spec_int ("minimum-key-length",
286                                                      P_("Minimum Key Length"),
287                                                      P_("Minimum length of the search key in order to look up matches"),
288                                                      0,
289                                                      G_MAXINT,
290                                                      1,
291                                                      GTK_PARAM_READWRITE));
292   /**
293    * GtkEntryCompletion:text-column:
294    *
295    * The column of the model containing the strings.
296    *
297    * Since: 2.6
298    */
299   g_object_class_install_property (object_class,
300                                    PROP_TEXT_COLUMN,
301                                    g_param_spec_int ("text-column",
302                                                      P_("Text column"),
303                                                      P_("The column of the model containing the strings."),
304                                                      -1,
305                                                      G_MAXINT,
306                                                      -1,
307                                                      GTK_PARAM_READWRITE));
308
309   /**
310    * GtkEntryCompletion:inline-completion:
311    * 
312    * Determines whether the common prefix of the possible completions 
313    * should be inserted automatically in the entry. Note that this
314    * requires text-column to be set, even if you are using a custom
315    * match function.
316    *
317    * Since: 2.6
318    **/
319   g_object_class_install_property (object_class,
320                                    PROP_INLINE_COMPLETION,
321                                    g_param_spec_boolean ("inline-completion",
322                                                          P_("Inline completion"),
323                                                          P_("Whether the common prefix should be inserted automatically"),
324                                                          FALSE,
325                                                          GTK_PARAM_READWRITE));
326   /**
327    * GtkEntryCompletion:popup-completion:
328    * 
329    * Determines whether the possible completions should be 
330    * shown in a popup window. 
331    *
332    * Since: 2.6
333    **/
334   g_object_class_install_property (object_class,
335                                    PROP_POPUP_COMPLETION,
336                                    g_param_spec_boolean ("popup-completion",
337                                                          P_("Popup completion"),
338                                                          P_("Whether the completions should be shown in a popup window"),
339                                                          TRUE,
340                                                          GTK_PARAM_READWRITE));
341
342   /**
343    * GtkEntryCompletion:popup-set-width:
344    * 
345    * Determines whether the completions popup window will be
346    * resized to the width of the entry.
347    *
348    * Since: 2.8
349    */
350   g_object_class_install_property (object_class,
351                                    PROP_POPUP_SET_WIDTH,
352                                    g_param_spec_boolean ("popup-set-width",
353                                                          P_("Popup set width"),
354                                                          P_("If TRUE, the popup window will have the same size as the entry"),
355                                                          TRUE,
356                                                          GTK_PARAM_READWRITE));
357
358   /**
359    * GtkEntryCompletion:popup-single-match:
360    * 
361    * Determines whether the completions popup window will shown
362    * for a single possible completion. You probably want to set
363    * this to %FALSE if you are using 
364    * <link linkend="GtkEntryCompletion--inline-completion">inline 
365    * completion</link>.
366    *
367    * Since: 2.8
368    */
369   g_object_class_install_property (object_class,
370                                    PROP_POPUP_SINGLE_MATCH,
371                                    g_param_spec_boolean ("popup-single-match",
372                                                          P_("Popup single match"),
373                                                          P_("If TRUE, the popup window will appear for a single match."),
374                                                          TRUE,
375                                                          GTK_PARAM_READWRITE));
376   /**
377    * GtkEntryCompletion:inline-selection:
378    * 
379    * Determines whether the possible completions on the popup
380    * will appear in the entry as you navigate through them.
381    
382    * Since: 2.12
383    */
384   g_object_class_install_property (object_class,
385                                    PROP_INLINE_SELECTION,
386                                    g_param_spec_boolean ("inline-selection",
387                                                          P_("Inline selection"),
388                                                          P_("Your description here"),
389                                                          FALSE,
390                                                          GTK_PARAM_READWRITE));
391
392   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
393 }
394
395 static void
396 gtk_entry_completion_buildable_init (GtkBuildableIface *iface)
397 {
398   iface->add_child = _gtk_cell_layout_buildable_add_child;
399   iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
400   iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
401 }
402
403 static void
404 gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
405 {
406   iface->pack_start = gtk_entry_completion_pack_start;
407   iface->pack_end = gtk_entry_completion_pack_end;
408   iface->clear = gtk_entry_completion_clear;
409   iface->add_attribute = gtk_entry_completion_add_attribute;
410   iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
411   iface->clear_attributes = gtk_entry_completion_clear_attributes;
412   iface->reorder = gtk_entry_completion_reorder;
413 }
414
415 static void
416 gtk_entry_completion_init (GtkEntryCompletion *completion)
417 {
418   GtkCellRenderer *cell;
419   GtkTreeSelection *sel;
420   GtkEntryCompletionPrivate *priv;
421   GtkWidget *popup_frame;
422
423   /* yes, also priv, need to keep the code readable */
424   priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
425
426   priv->minimum_key_length = 1;
427   priv->text_column = -1;
428   priv->has_completion = FALSE;
429   priv->inline_completion = FALSE;
430   priv->popup_completion = TRUE;
431   priv->popup_set_width = TRUE;
432   priv->popup_single_match = TRUE;
433   priv->inline_selection = FALSE;
434
435   /* completions */
436   priv->filter_model = NULL;
437
438   priv->tree_view = gtk_tree_view_new ();
439   g_signal_connect (priv->tree_view, "button_press_event",
440                     G_CALLBACK (gtk_entry_completion_list_button_press),
441                     completion);
442   g_signal_connect (priv->tree_view, "enter_notify_event",
443                     G_CALLBACK (gtk_entry_completion_list_enter_notify),
444                     completion);
445   g_signal_connect (priv->tree_view, "motion_notify_event",
446                     G_CALLBACK (gtk_entry_completion_list_motion_notify),
447                     completion);
448
449   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
450   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
451
452   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
453   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
454   gtk_tree_selection_unselect_all (sel);
455   g_signal_connect (sel, "changed",
456                     G_CALLBACK (gtk_entry_completion_selection_changed),
457                     completion);
458   priv->first_sel_changed = TRUE;
459
460   priv->column = gtk_tree_view_column_new ();
461   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
462
463   priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
464   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
465                                   GTK_POLICY_NEVER,
466                                   GTK_POLICY_AUTOMATIC);
467   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
468                                        GTK_SHADOW_NONE);
469
470   /* a nasty hack to get the completions treeview to size nicely */
471   gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
472
473   /* actions */
474   priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
475
476   priv->action_view =
477     gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
478   g_object_ref_sink (priv->action_view);
479   g_signal_connect (priv->action_view, "button_press_event",
480                     G_CALLBACK (gtk_entry_completion_action_button_press),
481                     completion);
482   g_signal_connect (priv->action_view, "enter_notify_event",
483                     G_CALLBACK (gtk_entry_completion_list_enter_notify),
484                     completion);
485   g_signal_connect (priv->action_view, "motion_notify_event",
486                     G_CALLBACK (gtk_entry_completion_list_motion_notify),
487                     completion);
488   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
489   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->action_view), TRUE);
490
491   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
492   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
493   gtk_tree_selection_unselect_all (sel);
494
495   cell = gtk_cell_renderer_text_new ();
496   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
497                                               0, "",
498                                               cell,
499                                               gtk_entry_completion_action_data_func,
500                                               NULL,
501                                               NULL);
502
503   /* pack it all */
504   priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
505   gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
506   g_signal_connect (priv->popup_window, "key_press_event",
507                     G_CALLBACK (gtk_entry_completion_popup_key_event),
508                     completion);
509   g_signal_connect (priv->popup_window, "key_release_event",
510                     G_CALLBACK (gtk_entry_completion_popup_key_event),
511                     completion);
512   g_signal_connect (priv->popup_window, "button_press_event",
513                     G_CALLBACK (gtk_entry_completion_popup_button_press),
514                     completion);
515
516   popup_frame = gtk_frame_new (NULL);
517   gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
518                              GTK_SHADOW_ETCHED_IN);
519   gtk_widget_show (popup_frame);
520   gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
521   
522   priv->vbox = gtk_vbox_new (FALSE, 0);
523   gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
524
525   gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
526   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
527                       TRUE, TRUE, 0);
528
529   /* we don't want to see the action treeview when no actions have
530    * been inserted, so we pack the action treeview after the first
531    * action has been added
532    */
533 }
534
535 static void
536 gtk_entry_completion_set_property (GObject      *object,
537                                    guint         prop_id,
538                                    const GValue *value,
539                                    GParamSpec   *pspec)
540 {
541   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
542   GtkEntryCompletionPrivate *priv = completion->priv;
543
544   switch (prop_id)
545     {
546       case PROP_MODEL:
547         gtk_entry_completion_set_model (completion,
548                                         g_value_get_object (value));
549         break;
550
551       case PROP_MINIMUM_KEY_LENGTH:
552         gtk_entry_completion_set_minimum_key_length (completion,
553                                                      g_value_get_int (value));
554         break;
555
556       case PROP_TEXT_COLUMN:
557         priv->text_column = g_value_get_int (value);
558         break;
559
560       case PROP_INLINE_COMPLETION:
561         priv->inline_completion = g_value_get_boolean (value);
562         break;
563
564       case PROP_POPUP_COMPLETION:
565         priv->popup_completion = g_value_get_boolean (value);
566         break;
567
568       case PROP_POPUP_SET_WIDTH:
569         priv->popup_set_width = g_value_get_boolean (value);
570         break;
571
572       case PROP_POPUP_SINGLE_MATCH:
573         priv->popup_single_match = g_value_get_boolean (value);
574         break;
575
576       case PROP_INLINE_SELECTION:
577         priv->inline_selection = g_value_get_boolean (value);
578         break;
579       
580       default:
581         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
582         break;
583     }
584 }
585
586 static void
587 gtk_entry_completion_get_property (GObject    *object,
588                                    guint       prop_id,
589                                    GValue     *value,
590                                    GParamSpec *pspec)
591 {
592   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
593
594   switch (prop_id)
595     {
596       case PROP_MODEL:
597         g_value_set_object (value,
598                             gtk_entry_completion_get_model (completion));
599         break;
600
601       case PROP_MINIMUM_KEY_LENGTH:
602         g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
603         break;
604
605       case PROP_TEXT_COLUMN:
606         g_value_set_int (value, gtk_entry_completion_get_text_column (completion));
607         break;
608
609       case PROP_INLINE_COMPLETION:
610         g_value_set_boolean (value, gtk_entry_completion_get_inline_completion (completion));
611         break;
612
613       case PROP_POPUP_COMPLETION:
614         g_value_set_boolean (value, gtk_entry_completion_get_popup_completion (completion));
615         break;
616
617       case PROP_POPUP_SET_WIDTH:
618         g_value_set_boolean (value, gtk_entry_completion_get_popup_set_width (completion));
619         break;
620
621       case PROP_POPUP_SINGLE_MATCH:
622         g_value_set_boolean (value, gtk_entry_completion_get_popup_single_match (completion));
623         break;
624
625       case PROP_INLINE_SELECTION:
626         g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion));
627         break;
628
629       default:
630         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
631         break;
632     }
633 }
634
635 static void
636 gtk_entry_completion_finalize (GObject *object)
637 {
638   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
639   GtkEntryCompletionPrivate *priv = completion->priv;
640
641   if (priv->tree_view)
642     gtk_widget_destroy (priv->tree_view);
643
644   if (priv->entry)
645     gtk_entry_set_completion (GTK_ENTRY (priv->entry), NULL);
646
647   if (priv->actions)
648     g_object_unref (priv->actions);
649   if (priv->action_view)
650     g_object_unref (priv->action_view);
651
652   g_free (priv->case_normalized_key);
653   g_free (priv->completion_prefix);
654
655   if (priv->popup_window)
656     gtk_widget_destroy (priv->popup_window);
657
658   if (priv->match_notify)
659     (* priv->match_notify) (priv->match_data);
660
661   G_OBJECT_CLASS (gtk_entry_completion_parent_class)->finalize (object);
662 }
663
664 /* implement cell layout interface */
665 static void
666 gtk_entry_completion_pack_start (GtkCellLayout   *cell_layout,
667                                  GtkCellRenderer *cell,
668                                  gboolean         expand)
669 {
670   GtkEntryCompletionPrivate *priv;
671
672   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
673
674   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
675
676   gtk_tree_view_column_pack_start (priv->column, cell, expand);
677 }
678
679 static void
680 gtk_entry_completion_pack_end (GtkCellLayout   *cell_layout,
681                                GtkCellRenderer *cell,
682                                gboolean         expand)
683 {
684   GtkEntryCompletionPrivate *priv;
685
686   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
687
688   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
689
690   gtk_tree_view_column_pack_end (priv->column, cell, expand);
691 }
692
693 static void
694 gtk_entry_completion_clear (GtkCellLayout *cell_layout)
695 {
696   GtkEntryCompletionPrivate *priv;
697
698   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
699
700   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
701
702   gtk_tree_view_column_clear (priv->column);
703 }
704
705 static void
706 gtk_entry_completion_add_attribute (GtkCellLayout   *cell_layout,
707                                     GtkCellRenderer *cell,
708                                     const gchar     *attribute,
709                                     gint             column)
710 {
711   GtkEntryCompletionPrivate *priv;
712
713   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
714
715   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
716
717   gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
718 }
719
720 static void
721 gtk_entry_completion_set_cell_data_func (GtkCellLayout          *cell_layout,
722                                          GtkCellRenderer        *cell,
723                                          GtkCellLayoutDataFunc   func,
724                                          gpointer                func_data,
725                                          GDestroyNotify          destroy)
726 {
727   GtkEntryCompletionPrivate *priv;
728
729   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
730
731   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
732
733   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
734                                       cell, func, func_data, destroy);
735 }
736
737 static void
738 gtk_entry_completion_clear_attributes (GtkCellLayout   *cell_layout,
739                                        GtkCellRenderer *cell)
740 {
741   GtkEntryCompletionPrivate *priv;
742
743   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
744
745   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
746
747   gtk_tree_view_column_clear_attributes (priv->column, cell);
748 }
749
750 static void
751 gtk_entry_completion_reorder (GtkCellLayout   *cell_layout,
752                               GtkCellRenderer *cell,
753                               gint             position)
754 {
755   GtkEntryCompletionPrivate *priv;
756
757   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
758
759   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
760
761   gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
762 }
763
764 /* all those callbacks */
765 static gboolean
766 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
767                                               const gchar        *key,
768                                               GtkTreeIter        *iter,
769                                               gpointer            user_data)
770 {
771   gchar *item = NULL;
772   gchar *normalized_string;
773   gchar *case_normalized_string;
774
775   gboolean ret = FALSE;
776
777   GtkTreeModel *model;
778
779   model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
780
781   g_return_val_if_fail (gtk_tree_model_get_column_type (model, completion->priv->text_column) == G_TYPE_STRING, 
782                         FALSE);
783
784   gtk_tree_model_get (model, iter,
785                       completion->priv->text_column, &item,
786                       -1);
787
788   if (item != NULL)
789     {
790       normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
791       case_normalized_string = g_utf8_casefold (normalized_string, -1);
792       
793       if (!strncmp (key, case_normalized_string, strlen (key)))
794         ret = TRUE;
795       
796       g_free (item);
797       g_free (normalized_string);
798       g_free (case_normalized_string);
799     }
800
801   return ret;
802 }
803
804 static gboolean
805 gtk_entry_completion_visible_func (GtkTreeModel *model,
806                                    GtkTreeIter  *iter,
807                                    gpointer      data)
808 {
809   gboolean ret = FALSE;
810
811   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
812
813   if (!completion->priv->case_normalized_key)
814     return ret;
815
816   if (completion->priv->match_func)
817     ret = (* completion->priv->match_func) (completion,
818                                             completion->priv->case_normalized_key,
819                                             iter,
820                                             completion->priv->match_data);
821   else if (completion->priv->text_column >= 0)
822     ret = gtk_entry_completion_default_completion_func (completion,
823                                                         completion->priv->case_normalized_key,
824                                                         iter,
825                                                         NULL);
826
827   return ret;
828 }
829
830 static gboolean
831 gtk_entry_completion_popup_key_event (GtkWidget   *widget,
832                                       GdkEventKey *event,
833                                       gpointer     user_data)
834 {
835   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
836
837   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
838     return FALSE;
839
840   /* propagate event to the entry */
841   gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
842
843   return TRUE;
844 }
845
846 static gboolean
847 gtk_entry_completion_popup_button_press (GtkWidget      *widget,
848                                          GdkEventButton *event,
849                                          gpointer        user_data)
850 {
851   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
852
853   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
854     return FALSE;
855
856   /* if we come here, it's usually time to popdown */
857   _gtk_entry_completion_popdown (completion);
858
859   return TRUE;
860 }
861
862 static gboolean
863 gtk_entry_completion_list_button_press (GtkWidget      *widget,
864                                         GdkEventButton *event,
865                                         gpointer        user_data)
866 {
867   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
868   GtkTreePath *path = NULL;
869
870   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
871     return FALSE;
872
873   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
874                                      event->x, event->y,
875                                      &path, NULL, NULL, NULL))
876     {
877       GtkTreeIter iter;
878       gboolean entry_set;
879
880       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
881                                &iter, path);
882       gtk_tree_path_free (path);
883
884       g_signal_handler_block (completion->priv->entry,
885                               completion->priv->changed_id);
886       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
887                      0, GTK_TREE_MODEL (completion->priv->filter_model),
888                      &iter, &entry_set);
889       g_signal_handler_unblock (completion->priv->entry,
890                                 completion->priv->changed_id);
891
892       _gtk_entry_completion_popdown (completion);
893
894       return TRUE;
895     }
896
897   return FALSE;
898 }
899
900 static gboolean
901 gtk_entry_completion_action_button_press (GtkWidget      *widget,
902                                           GdkEventButton *event,
903                                           gpointer        user_data)
904 {
905   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
906   GtkTreePath *path = NULL;
907
908   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
909     return FALSE;
910
911   _gtk_entry_reset_im_context (GTK_ENTRY (completion->priv->entry));
912
913   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
914                                      event->x, event->y,
915                                      &path, NULL, NULL, NULL))
916     {
917       g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
918                      0, gtk_tree_path_get_indices (path)[0]);
919       gtk_tree_path_free (path);
920
921       _gtk_entry_completion_popdown (completion);
922       return TRUE;
923     }
924
925   return FALSE;
926 }
927
928 static void
929 gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
930                                        GtkCellRenderer   *cell,
931                                        GtkTreeModel      *model,
932                                        GtkTreeIter       *iter,
933                                        gpointer           data)
934 {
935   gchar *string = NULL;
936   gboolean markup;
937
938   gtk_tree_model_get (model, iter,
939                       0, &string,
940                       1, &markup,
941                       -1);
942
943   if (!string)
944     return;
945
946   if (markup)
947     g_object_set (cell,
948                   "text", NULL,
949                   "markup", string,
950                   NULL);
951   else
952     g_object_set (cell,
953                   "markup", NULL,
954                   "text", string,
955                   NULL);
956
957   g_free (string);
958 }
959
960 static void
961 gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
962                                         gpointer          data)
963 {
964   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
965
966   if (completion->priv->first_sel_changed)
967     {
968       completion->priv->first_sel_changed = FALSE;
969       if (gtk_widget_is_focus (completion->priv->tree_view))
970         gtk_tree_selection_unselect_all (selection);
971     }
972 }
973
974 /* public API */
975
976 /**
977  * gtk_entry_completion_new:
978  *
979  * Creates a new #GtkEntryCompletion object.
980  *
981  * Return value: A newly created #GtkEntryCompletion object.
982  *
983  * Since: 2.4
984  */
985 GtkEntryCompletion *
986 gtk_entry_completion_new (void)
987 {
988   GtkEntryCompletion *completion;
989
990   completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
991
992   return completion;
993 }
994
995 /**
996  * gtk_entry_completion_get_entry:
997  * @completion: A #GtkEntryCompletion.
998  *
999  * Gets the entry @completion has been attached to.
1000  *
1001  * Return value: The entry @completion has been attached to.
1002  *
1003  * Since: 2.4
1004  */
1005 GtkWidget *
1006 gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
1007 {
1008   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1009
1010   return completion->priv->entry;
1011 }
1012
1013 /**
1014  * gtk_entry_completion_set_model:
1015  * @completion: A #GtkEntryCompletion.
1016  * @model: The #GtkTreeModel.
1017  *
1018  * Sets the model for a #GtkEntryCompletion. If @completion already has
1019  * a model set, it will remove it before setting the new model.
1020  * If model is %NULL, then it will unset the model.
1021  *
1022  * Since: 2.4
1023  */
1024 void
1025 gtk_entry_completion_set_model (GtkEntryCompletion *completion,
1026                                 GtkTreeModel       *model)
1027 {
1028   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1029   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1030
1031   if (!model)
1032     {
1033       gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
1034                                NULL);
1035       _gtk_entry_completion_popdown (completion);
1036       completion->priv->filter_model = NULL;
1037       return;
1038     }
1039      
1040   /* code will unref the old filter model (if any) */
1041   completion->priv->filter_model =
1042     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
1043   gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
1044                                           gtk_entry_completion_visible_func,
1045                                           completion,
1046                                           NULL);
1047
1048   gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
1049                            GTK_TREE_MODEL (completion->priv->filter_model));
1050   g_object_unref (completion->priv->filter_model);
1051
1052   g_object_notify (G_OBJECT (completion), "model");
1053
1054   if (GTK_WIDGET_VISIBLE (completion->priv->popup_window))
1055     _gtk_entry_completion_resize_popup (completion);
1056 }
1057
1058 /**
1059  * gtk_entry_completion_get_model:
1060  * @completion: A #GtkEntryCompletion.
1061  *
1062  * Returns the model the #GtkEntryCompletion is using as data source.
1063  * Returns %NULL if the model is unset.
1064  *
1065  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
1066  *
1067  * Since: 2.4
1068  */
1069 GtkTreeModel *
1070 gtk_entry_completion_get_model (GtkEntryCompletion *completion)
1071 {
1072   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1073
1074   if (!completion->priv->filter_model)
1075     return NULL;
1076   
1077   return gtk_tree_model_filter_get_model (completion->priv->filter_model);
1078 }
1079
1080 /**
1081  * gtk_entry_completion_set_match_func:
1082  * @completion: A #GtkEntryCompletion.
1083  * @func: The #GtkEntryCompletionMatchFunc to use.
1084  * @func_data: The user data for @func.
1085  * @func_notify: Destroy notifier for @func_data.
1086  *
1087  * Sets the match function for @completion to be @func. The match function
1088  * is used to determine if a row should or should not be in the completion
1089  * list.
1090  *
1091  * Since: 2.4
1092  */
1093 void
1094 gtk_entry_completion_set_match_func (GtkEntryCompletion          *completion,
1095                                      GtkEntryCompletionMatchFunc  func,
1096                                      gpointer                     func_data,
1097                                      GDestroyNotify               func_notify)
1098 {
1099   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1100
1101   if (completion->priv->match_notify)
1102     (* completion->priv->match_notify) (completion->priv->match_data);
1103
1104   completion->priv->match_func = func;
1105   completion->priv->match_data = func_data;
1106   completion->priv->match_notify = func_notify;
1107 }
1108
1109 /**
1110  * gtk_entry_completion_set_minimum_key_length:
1111  * @completion: A #GtkEntryCompletion.
1112  * @length: The minimum length of the key in order to start completing.
1113  *
1114  * Requires the length of the search key for @completion to be at least
1115  * @length. This is useful for long lists, where completing using a small
1116  * key takes a lot of time and will come up with meaningless results anyway
1117  * (ie, a too large dataset).
1118  *
1119  * Since: 2.4
1120  */
1121 void
1122 gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
1123                                              gint                length)
1124 {
1125   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1126   g_return_if_fail (length >= 0);
1127
1128   if (completion->priv->minimum_key_length != length)
1129     {
1130       completion->priv->minimum_key_length = length;
1131      
1132       g_object_notify (G_OBJECT (completion), "minimum-key-length");
1133     }
1134 }
1135
1136 /**
1137  * gtk_entry_completion_get_minimum_key_length:
1138  * @completion: A #GtkEntryCompletion.
1139  *
1140  * Returns the minimum key length as set for @completion.
1141  *
1142  * Return value: The currently used minimum key length.
1143  *
1144  * Since: 2.4
1145  */
1146 gint
1147 gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
1148 {
1149   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
1150
1151   return completion->priv->minimum_key_length;
1152 }
1153
1154 /**
1155  * gtk_entry_completion_complete:
1156  * @completion: A #GtkEntryCompletion.
1157  *
1158  * Requests a completion operation, or in other words a refiltering of the
1159  * current list with completions, using the current key. The completion list
1160  * view will be updated accordingly.
1161  *
1162  * Since: 2.4
1163  */
1164 void
1165 gtk_entry_completion_complete (GtkEntryCompletion *completion)
1166 {
1167   gchar *tmp;
1168
1169   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1170
1171   if (!completion->priv->filter_model)
1172     return;
1173   
1174   g_free (completion->priv->case_normalized_key);
1175
1176   tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
1177                           -1, G_NORMALIZE_ALL);
1178   completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
1179   g_free (tmp);
1180
1181   gtk_tree_model_filter_refilter (completion->priv->filter_model);
1182
1183   if (GTK_WIDGET_VISIBLE (completion->priv->popup_window))
1184     _gtk_entry_completion_resize_popup (completion);
1185 }
1186
1187 static void
1188 gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
1189                                     gint                index,
1190                                     const gchar        *string,
1191                                     gboolean            markup)
1192 {
1193   GtkTreeIter iter;
1194
1195   gtk_list_store_insert (completion->priv->actions, &iter, index);
1196   gtk_list_store_set (completion->priv->actions, &iter,
1197                       0, string,
1198                       1, markup,
1199                       -1);
1200
1201   if (!completion->priv->action_view->parent)
1202     {
1203       GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
1204
1205       gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
1206                                 path, NULL, FALSE);
1207       gtk_tree_path_free (path);
1208
1209       gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
1210                           completion->priv->action_view, FALSE, FALSE, 0);
1211       gtk_widget_show (completion->priv->action_view);
1212     }
1213 }
1214
1215 /**
1216  * gtk_entry_completion_insert_action_text:
1217  * @completion: A #GtkEntryCompletion.
1218  * @index_: The index of the item to insert.
1219  * @text: Text of the item to insert.
1220  *
1221  * Inserts an action in @completion's action item list at position @index_
1222  * with text @text. If you want the action item to have markup, use
1223  * gtk_entry_completion_insert_action_markup().
1224  *
1225  * Since: 2.4
1226  */
1227 void
1228 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
1229                                          gint                index_,
1230                                          const gchar        *text)
1231 {
1232   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1233   g_return_if_fail (text != NULL);
1234
1235   gtk_entry_completion_insert_action (completion, index_, text, FALSE);
1236 }
1237
1238 /**
1239  * gtk_entry_completion_insert_action_markup:
1240  * @completion: A #GtkEntryCompletion.
1241  * @index_: The index of the item to insert.
1242  * @markup: Markup of the item to insert.
1243  *
1244  * Inserts an action in @completion's action item list at position @index_
1245  * with markup @markup.
1246  *
1247  * Since: 2.4
1248  */
1249 void
1250 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
1251                                            gint                index_,
1252                                            const gchar        *markup)
1253 {
1254   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1255   g_return_if_fail (markup != NULL);
1256
1257   gtk_entry_completion_insert_action (completion, index_, markup, TRUE);
1258 }
1259
1260 /**
1261  * gtk_entry_completion_delete_action:
1262  * @completion: A #GtkEntryCompletion.
1263  * @index_: The index of the item to Delete.
1264  *
1265  * Deletes the action at @index_ from @completion's action list.
1266  *
1267  * Since: 2.4
1268  */
1269 void
1270 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
1271                                     gint                index_)
1272 {
1273   GtkTreeIter iter;
1274
1275   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1276   g_return_if_fail (index_ >= 0);
1277
1278   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
1279                                  &iter, NULL, index_);
1280   gtk_list_store_remove (completion->priv->actions, &iter);
1281 }
1282
1283 /**
1284  * gtk_entry_completion_set_text_column:
1285  * @completion: A #GtkEntryCompletion.
1286  * @column: The column in the model of @completion to get strings from.
1287  *
1288  * Convenience function for setting up the most used case of this code: a
1289  * completion list with just strings. This function will set up @completion
1290  * to have a list displaying all (and just) strings in the completion list,
1291  * and to get those strings from @column in the model of @completion.
1292  *
1293  * This functions creates and adds a #GtkCellRendererText for the selected 
1294  * column. If you need to set the text column, but don't want the cell 
1295  * renderer, use g_object_set() to set the ::text_column property directly.
1296  * 
1297  * Since: 2.4
1298  */
1299 void
1300 gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
1301                                       gint                column)
1302 {
1303   GtkCellRenderer *cell;
1304
1305   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1306   g_return_if_fail (column >= 0);
1307
1308   completion->priv->text_column = column;
1309
1310   cell = gtk_cell_renderer_text_new ();
1311   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1312                               cell, TRUE);
1313   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1314                                  cell,
1315                                  "text", column);
1316
1317   g_object_notify (G_OBJECT (completion), "text-column");
1318 }
1319
1320 /**
1321  * gtk_entry_completion_get_text_column:
1322  * @completion: a #GtkEntryCompletion
1323  * 
1324  * Returns the column in the model of @completion to get strings from.
1325  * 
1326  * Return value: the column containing the strings
1327  *
1328  * Since: 2.6
1329  **/
1330 gint
1331 gtk_entry_completion_get_text_column (GtkEntryCompletion *completion)
1332 {
1333   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), -1);
1334
1335   return completion->priv->text_column;  
1336 }
1337
1338 /* private */
1339
1340 static gboolean
1341 gtk_entry_completion_list_enter_notify (GtkWidget        *widget,
1342                                         GdkEventCrossing *event,
1343                                         gpointer          data)
1344 {
1345   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1346   
1347   return completion->priv->ignore_enter;
1348 }
1349
1350 static gboolean
1351 gtk_entry_completion_list_motion_notify (GtkWidget      *widget,
1352                                          GdkEventMotion *event,
1353                                          gpointer        data)
1354 {
1355   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1356
1357   completion->priv->ignore_enter = FALSE;
1358   
1359   return FALSE;
1360 }
1361
1362
1363 /* some nasty size requisition */
1364 gboolean
1365 _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
1366 {
1367   gint x, y;
1368   gint matches, items, height, x_border, y_border;
1369   GdkScreen *screen;
1370   gint monitor_num;
1371   gint vertical_separator;
1372   GdkRectangle monitor;
1373   GtkRequisition popup_req;
1374   GtkRequisition entry_req;
1375   GtkTreePath *path;
1376   gboolean above;
1377   gint width;
1378
1379   if (!completion->priv->entry->window)
1380     return FALSE;
1381
1382   gdk_window_get_origin (completion->priv->entry->window, &x, &y);
1383   _gtk_entry_get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
1384
1385   matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
1386
1387   items = MIN (matches, 15);
1388
1389   gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
1390                                       NULL, NULL, NULL, &height);
1391
1392   gtk_widget_style_get (GTK_WIDGET (completion->priv->tree_view),
1393                         "vertical-separator", &vertical_separator,
1394                         NULL);
1395
1396   height += vertical_separator;
1397   
1398   gtk_widget_realize (completion->priv->tree_view);
1399
1400   if (items <= 0)
1401     gtk_widget_hide (completion->priv->scrolled_window);
1402   else
1403     gtk_widget_show (completion->priv->scrolled_window);
1404
1405   screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
1406   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1407                                                   GTK_WIDGET (completion->priv->entry)->window);
1408   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1409
1410   if (completion->priv->popup_set_width)
1411     width = MIN (completion->priv->entry->allocation.width, monitor.width) - 2 * x_border;
1412   else
1413     width = -1;
1414
1415   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (completion->priv->tree_view));
1416   gtk_widget_set_size_request (completion->priv->tree_view, width, items * height);
1417
1418   /* default on no match */
1419   completion->priv->current_selected = -1;
1420
1421   items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
1422
1423   if (items)
1424     {
1425       gtk_widget_show (completion->priv->action_view);
1426       gtk_widget_set_size_request (completion->priv->action_view, width, -1);
1427     }
1428   else
1429     gtk_widget_hide (completion->priv->action_view);
1430
1431   gtk_widget_size_request (completion->priv->popup_window, &popup_req);
1432   gtk_widget_size_request (completion->priv->entry, &entry_req);
1433   
1434   if (x < monitor.x)
1435     x = monitor.x;
1436   else if (x + popup_req.width > monitor.x + monitor.width)
1437     x = monitor.x + monitor.width - popup_req.width;
1438   
1439   if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height ||
1440       y - monitor.y < (monitor.y + monitor.height) - (y + entry_req.height))
1441     {
1442       y += entry_req.height;
1443       above = FALSE;
1444     }
1445   else
1446     {
1447       y -= popup_req.height;
1448       above = TRUE;
1449     }
1450   
1451   if (matches > 0) 
1452     {
1453       path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
1454       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path, 
1455                                     NULL, FALSE, 0.0, 0.0);
1456       gtk_tree_path_free (path);
1457     }
1458
1459   gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
1460
1461   return above;
1462 }
1463
1464 void
1465 _gtk_entry_completion_popup (GtkEntryCompletion *completion)
1466 {
1467   GtkTreeViewColumn *column;
1468   GList *renderers;
1469   GtkWidget *toplevel;
1470
1471   if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
1472     return;
1473
1474   if (!GTK_WIDGET_MAPPED (completion->priv->entry))
1475     return;
1476
1477   if (!GTK_WIDGET_HAS_FOCUS (completion->priv->entry))
1478     return;
1479
1480   completion->priv->ignore_enter = TRUE;
1481     
1482   column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1483   renderers = gtk_tree_view_column_get_cell_renderers (column);
1484   gtk_widget_ensure_style (completion->priv->tree_view);
1485   g_object_set (GTK_CELL_RENDERER (renderers->data), "cell-background-gdk",
1486                 &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
1487                 NULL);
1488   g_list_free (renderers);
1489
1490   gtk_widget_show_all (completion->priv->vbox);
1491
1492   _gtk_entry_completion_resize_popup (completion);
1493
1494   toplevel = gtk_widget_get_toplevel (completion->priv->entry);
1495   if (GTK_IS_WINDOW (toplevel))
1496     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), 
1497                                  GTK_WINDOW (completion->priv->popup_window));
1498
1499   /* prevent the first row being focused */
1500   gtk_widget_grab_focus (completion->priv->tree_view);
1501
1502   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
1503   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
1504   
1505
1506   gtk_widget_show (completion->priv->popup_window);
1507     
1508   gtk_grab_add (completion->priv->popup_window);
1509   gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
1510                     GDK_BUTTON_PRESS_MASK |
1511                     GDK_BUTTON_RELEASE_MASK |
1512                     GDK_POINTER_MOTION_MASK,
1513                     NULL, NULL, GDK_CURRENT_TIME);
1514 }
1515
1516 void
1517 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
1518 {
1519   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
1520     return;
1521
1522   completion->priv->ignore_enter = FALSE;
1523   
1524   gdk_pointer_ungrab (GDK_CURRENT_TIME);
1525   gtk_grab_remove (completion->priv->popup_window);
1526
1527   gtk_widget_hide (completion->priv->popup_window);
1528 }
1529
1530 static gboolean 
1531 gtk_entry_completion_match_selected (GtkEntryCompletion *completion,
1532                                      GtkTreeModel       *model,
1533                                      GtkTreeIter        *iter)
1534 {
1535   gchar *str = NULL;
1536
1537   gtk_tree_model_get (model, iter, completion->priv->text_column, &str, -1);
1538   gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str ? str : "");
1539   
1540   /* move cursor to the end */
1541   gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry), -1);
1542   
1543   g_free (str);
1544
1545   return TRUE;
1546 }
1547
1548 static gboolean
1549 gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion,
1550                                       GtkTreeModel       *model,
1551                                       GtkTreeIter        *iter)
1552 {
1553   gtk_entry_completion_insert_completion (completion, model, iter);
1554
1555   return TRUE;
1556 }
1557
1558 static gchar *
1559 gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion)
1560 {
1561   GtkTreeIter iter;
1562   gchar *prefix = NULL;
1563   gboolean valid;
1564   const gchar *key;
1565
1566   if (completion->priv->text_column < 0)
1567     return NULL;
1568
1569   key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
1570
1571   valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (completion->priv->filter_model),
1572                                          &iter);
1573   
1574   while (valid)
1575     {
1576       gchar *text;
1577       
1578       gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
1579                           &iter, completion->priv->text_column, &text,
1580                           -1);
1581
1582       if (text && g_str_has_prefix (text, key))
1583         {
1584           if (!prefix)
1585             prefix = g_strdup (text);
1586           else
1587             {
1588               gchar *p = prefix;
1589               gchar *q = text;
1590               
1591               while (*p && *p == *q)
1592                 {
1593                   p++;
1594                   q++;
1595                 }
1596               
1597               *p = '\0';
1598               
1599               if (p > prefix)
1600                 {
1601                   /* strip a partial multibyte character */
1602                   q = g_utf8_find_prev_char (prefix, p);
1603                   switch (g_utf8_get_char_validated (q, p - q))
1604                     {
1605                     case (gunichar)-2:
1606                     case (gunichar)-1:
1607                       *q = 0;
1608                     default: ;
1609                     }
1610                 }
1611             }
1612         }
1613       
1614       g_free (text);
1615       valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (completion->priv->filter_model),
1616                                         &iter);
1617     }
1618
1619   return prefix;
1620 }
1621
1622
1623 static gboolean
1624 gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
1625                                          const gchar        *prefix)
1626 {
1627   if (prefix)
1628     {
1629       gint key_len;
1630       gint prefix_len;
1631       const gchar *key;
1632
1633       prefix_len = g_utf8_strlen (prefix, -1);
1634
1635       key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
1636       key_len = g_utf8_strlen (key, -1);
1637
1638       if (completion->priv->completion_prefix == NULL)
1639         completion->priv->completion_prefix = g_strdup (key);
1640
1641       if (prefix_len > key_len)
1642         {
1643           gint pos = prefix_len;
1644
1645           gtk_editable_insert_text (GTK_EDITABLE (completion->priv->entry),
1646                                     prefix + strlen (key), -1, &pos);
1647           gtk_editable_select_region (GTK_EDITABLE (completion->priv->entry),
1648                                       key_len, prefix_len);
1649
1650           completion->priv->has_completion = TRUE;
1651         }
1652     }
1653
1654   return TRUE;
1655 }
1656
1657 /**
1658  * gtk_entry_completion_get_completion_prefix:
1659  * @completion: a #GtkEntryCompletion
1660  * 
1661  * Get the original text entered by the user that triggered
1662  * the completion or %NULL if there's no completion ongoing.
1663  * 
1664  * Returns: the prefix for the current completion
1665  * 
1666  * Since: 2.12
1667  **/
1668 const gchar*
1669 gtk_entry_completion_get_completion_prefix (GtkEntryCompletion *completion)
1670 {
1671   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1672
1673   return completion->priv->completion_prefix;
1674 }
1675
1676 static void
1677 gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
1678                                              const gchar *text)
1679 {
1680   GtkEntryCompletionPrivate *priv = completion->priv;
1681   gint len;
1682
1683   priv = completion->priv;
1684
1685   if (priv->changed_id > 0)
1686     g_signal_handler_block (priv->entry, priv->changed_id);
1687
1688   if (priv->insert_text_id > 0)
1689     g_signal_handler_block (priv->entry, priv->insert_text_id);
1690
1691   if (completion->priv->completion_prefix == NULL)
1692     completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
1693
1694   gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
1695
1696   len = strlen (priv->completion_prefix);
1697   gtk_editable_select_region (GTK_EDITABLE (priv->entry), len, -1);
1698
1699   if (priv->changed_id > 0)
1700     g_signal_handler_unblock (priv->entry, priv->changed_id);
1701
1702   if (priv->insert_text_id > 0)
1703     g_signal_handler_unblock (priv->entry, priv->insert_text_id);
1704 }
1705
1706 static gboolean
1707 gtk_entry_completion_insert_completion (GtkEntryCompletion *completion,
1708                                         GtkTreeModel       *model,
1709                                         GtkTreeIter        *iter)
1710 {
1711   gchar *str = NULL;
1712
1713   if (completion->priv->text_column < 0)
1714     return FALSE;
1715
1716   gtk_tree_model_get (model, iter,
1717                       completion->priv->text_column, &str,
1718                       -1);
1719
1720   gtk_entry_completion_insert_completion_text (completion, str);
1721
1722   g_free (str);
1723
1724   return TRUE;
1725 }
1726
1727 /**
1728  * gtk_entry_completion_insert_prefix:
1729  * @completion: a #GtkEntryCompletion
1730  * 
1731  * Requests a prefix insertion. 
1732  * 
1733  * Since: 2.6
1734  **/
1735
1736 void
1737 gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
1738 {
1739   gboolean done;
1740   gchar *prefix;
1741
1742   if (completion->priv->insert_text_id > 0)
1743     g_signal_handler_block (completion->priv->entry,
1744                             completion->priv->insert_text_id);
1745
1746   prefix = gtk_entry_completion_compute_prefix (completion);
1747   if (prefix)
1748     {
1749       g_signal_emit (completion, entry_completion_signals[INSERT_PREFIX],
1750                      0, prefix, &done);
1751       g_free (prefix);
1752     }
1753
1754   if (completion->priv->insert_text_id > 0)
1755     g_signal_handler_unblock (completion->priv->entry,
1756                               completion->priv->insert_text_id);
1757 }
1758
1759 /**
1760  * gtk_entry_completion_set_inline_completion:
1761  * @completion: a #GtkEntryCompletion
1762  * @inline_completion: %TRUE to do inline completion
1763  * 
1764  * Sets whether the common prefix of the possible completions should
1765  * be automatically inserted in the entry.
1766  * 
1767  * Since: 2.6
1768  **/
1769 void 
1770 gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
1771                                             gboolean            inline_completion)
1772 {
1773   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1774   
1775   inline_completion = inline_completion != FALSE;
1776
1777   if (completion->priv->inline_completion != inline_completion)
1778     {
1779       completion->priv->inline_completion = inline_completion;
1780
1781       g_object_notify (G_OBJECT (completion), "inline-completion");
1782     }
1783 }
1784
1785 /**
1786  * gtk_entry_completion_get_inline_completion:
1787  * @completion: a #GtkEntryCompletion
1788  * 
1789  * Returns whether the common prefix of the possible completions should
1790  * be automatically inserted in the entry.
1791  * 
1792  * Return value: %TRUE if inline completion is turned on
1793  * 
1794  * Since: 2.6
1795  **/
1796 gboolean
1797 gtk_entry_completion_get_inline_completion (GtkEntryCompletion *completion)
1798 {
1799   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1800   
1801   return completion->priv->inline_completion;
1802 }
1803
1804 /**
1805  * gtk_entry_completion_set_popup_completion:
1806  * @completion: a #GtkEntryCompletion
1807  * @popup_completion: %TRUE to do popup completion
1808  * 
1809  * Sets whether the completions should be presented in a popup window.
1810  * 
1811  * Since: 2.6
1812  **/
1813 void 
1814 gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion,
1815                                            gboolean            popup_completion)
1816 {
1817   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1818   
1819   popup_completion = popup_completion != FALSE;
1820
1821   if (completion->priv->popup_completion != popup_completion)
1822     {
1823       completion->priv->popup_completion = popup_completion;
1824
1825       g_object_notify (G_OBJECT (completion), "popup-completion");
1826     }
1827 }
1828
1829
1830 /**
1831  * gtk_entry_completion_get_popup_completion:
1832  * @completion: a #GtkEntryCompletion
1833  * 
1834  * Returns whether the completions should be presented in a popup window.
1835  * 
1836  * Return value: %TRUE if popup completion is turned on
1837  * 
1838  * Since: 2.6
1839  **/
1840 gboolean
1841 gtk_entry_completion_get_popup_completion (GtkEntryCompletion *completion)
1842 {
1843   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1844   
1845   return completion->priv->popup_completion;
1846 }
1847
1848 /**
1849  * gtk_entry_completion_set_popup_set_width:
1850  * @completion: a #GtkEntryCompletion
1851  * @popup_set_width: %TRUE to make the width of the popup the same as the entry
1852  *
1853  * Sets whether the completion popup window will be resized to be the same
1854  * width as the entry.
1855  *
1856  * Since: 2.8
1857  */
1858 void 
1859 gtk_entry_completion_set_popup_set_width (GtkEntryCompletion *completion,
1860                                           gboolean            popup_set_width)
1861 {
1862   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1863   
1864   popup_set_width = popup_set_width != FALSE;
1865
1866   if (completion->priv->popup_set_width != popup_set_width)
1867     {
1868       completion->priv->popup_set_width = popup_set_width;
1869
1870       g_object_notify (G_OBJECT (completion), "popup-set-width");
1871     }
1872 }
1873
1874 /**
1875  * gtk_entry_completion_get_popup_set_width:
1876  * @completion: a #GtkEntryCompletion
1877  * 
1878  * Returns whether the  completion popup window will be resized to the 
1879  * width of the entry.
1880  * 
1881  * Return value: %TRUE if the popup window will be resized to the width of 
1882  *   the entry
1883  * 
1884  * Since: 2.8
1885  **/
1886 gboolean
1887 gtk_entry_completion_get_popup_set_width (GtkEntryCompletion *completion)
1888 {
1889   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1890   
1891   return completion->priv->popup_set_width;
1892 }
1893
1894
1895 /**
1896  * gtk_entry_completion_set_popup_single_match:
1897  * @completion: a #GtkEntryCompletion
1898  * @popup_single_match: %TRUE if the popup should appear even for a single
1899  *   match
1900  *
1901  * Sets whether the completion popup window will appear even if there is
1902  * only a single match. You may want to set this to %FALSE if you
1903  * are using <link linkend="GtkEntryCompletion--inline-completion">inline
1904  * completion</link>.
1905  *
1906  * Since: 2.8
1907  */
1908 void 
1909 gtk_entry_completion_set_popup_single_match (GtkEntryCompletion *completion,
1910                                              gboolean            popup_single_match)
1911 {
1912   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1913   
1914   popup_single_match = popup_single_match != FALSE;
1915
1916   if (completion->priv->popup_single_match != popup_single_match)
1917     {
1918       completion->priv->popup_single_match = popup_single_match;
1919
1920       g_object_notify (G_OBJECT (completion), "popup-single-match");
1921     }
1922 }
1923
1924 /**
1925  * gtk_entry_completion_get_popup_single_match:
1926  * @completion: a #GtkEntryCompletion
1927  * 
1928  * Returns whether the completion popup window will appear even if there is
1929  * only a single match. 
1930  * 
1931  * Return value: %TRUE if the popup window will appear regardless of the
1932  *    number of matches.
1933  * 
1934  * Since: 2.8
1935  **/
1936 gboolean
1937 gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion)
1938 {
1939   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1940   
1941   return completion->priv->popup_single_match;
1942 }
1943
1944 /**
1945  * gtk_entry_completion_set_inline_selection:
1946  * @completion: a #GtkEntryCompletion
1947  * @inline_selection: %TRUE to do inline selection
1948  * 
1949  * Sets whether it is possible to cycle through the possible completions
1950  * inside the entry.
1951  * 
1952  * Since: 2.12
1953  **/
1954 void
1955 gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion,
1956                                            gboolean inline_selection)
1957 {
1958   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1959
1960   inline_selection = inline_selection != FALSE;
1961
1962   if (completion->priv->inline_selection != inline_selection)
1963     {
1964       completion->priv->inline_selection = inline_selection;
1965
1966       g_object_notify (G_OBJECT (completion), "inline-selection");
1967     }
1968 }
1969
1970 /**
1971  * gtk_entry_completion_get_inline_selection:
1972  * @completion: a #GtkEntryCompletion
1973  *
1974  * Returns %TRUE if inline-selection mode is turned on.
1975  *
1976  * Returns: %TRUE if inline-selection mode is on
1977  *
1978  * Since: 2.12
1979  **/
1980 gboolean
1981 gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion)
1982 {
1983   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1984
1985   return completion->priv->inline_selection;
1986 }
1987
1988 #define __GTK_ENTRY_COMPLETION_C__
1989 #include "gtkaliasdef.c"