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