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