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