]> Pileus Git - ~andy/gtk/blob - gtk/gtkentrycompletion.c
34fcb7a696d8fbf1d71d810dcaf162da06cc01b4
[~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 <string.h>
39
40
41 /* signals */
42 enum
43 {
44   MATCH_SELECTED,
45   ACTION_ACTIVATED,
46   LAST_SIGNAL
47 };
48
49 /* properties */
50 enum
51 {
52   PROP_0,
53   PROP_MODEL,
54   PROP_MINIMUM_KEY_LENGTH
55 };
56
57 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
58
59 static void     gtk_entry_completion_class_init          (GtkEntryCompletionClass *klass);
60 static void     gtk_entry_completion_cell_layout_init    (GtkCellLayoutIface      *iface);
61 static void     gtk_entry_completion_init                (GtkEntryCompletion      *completion);
62 static void     gtk_entry_completion_set_property        (GObject                 *object,
63                                                           guint                    prop_id,
64                                                           const GValue            *value,
65                                                           GParamSpec              *pspec);
66 static void     gtk_entry_completion_get_property        (GObject                 *object,
67                                                           guint                    prop_id,
68                                                           GValue                  *value,
69                                                           GParamSpec              *pspec);
70 static void     gtk_entry_completion_finalize            (GObject                 *object);
71
72 static void     gtk_entry_completion_pack_start          (GtkCellLayout           *cell_layout,
73                                                           GtkCellRenderer         *cell,
74                                                           gboolean                 expand);
75 static void     gtk_entry_completion_pack_end            (GtkCellLayout           *cell_layout,
76                                                           GtkCellRenderer         *cell,
77                                                           gboolean                 expand);
78 static void     gtk_entry_completion_clear               (GtkCellLayout           *cell_layout);
79 static void     gtk_entry_completion_add_attribute       (GtkCellLayout           *cell_layout,
80                                                           GtkCellRenderer         *cell,
81                                                           const char              *attribute,
82                                                           gint                     column);
83 static void     gtk_entry_completion_set_cell_data_func  (GtkCellLayout           *cell_layout,
84                                                           GtkCellRenderer         *cell,
85                                                           GtkCellLayoutDataFunc    func,
86                                                           gpointer                 func_data,
87                                                           GDestroyNotify           destroy);
88 static void     gtk_entry_completion_clear_attributes    (GtkCellLayout           *cell_layout,
89                                                           GtkCellRenderer         *cell);
90 static void     gtk_entry_completion_reorder             (GtkCellLayout           *cell_layout,
91                                                           GtkCellRenderer         *cell,
92                                                           gint                     position);
93
94 static gboolean gtk_entry_completion_visible_func        (GtkTreeModel            *model,
95                                                           GtkTreeIter             *iter,
96                                                           gpointer                 data);
97 static gboolean gtk_entry_completion_popup_key_press     (GtkWidget               *widget,
98                                                           GdkEventKey             *event,
99                                                           gpointer                 user_data);
100 static gboolean gtk_entry_completion_popup_button_press  (GtkWidget               *widget,
101                                                           GdkEventButton          *event,
102                                                           gpointer                 user_data);
103 static gboolean gtk_entry_completion_list_button_press   (GtkWidget               *widget,
104                                                           GdkEventButton          *event,
105                                                           gpointer                 user_data);
106 static gboolean gtk_entry_completion_action_button_press (GtkWidget               *widget,
107                                                           GdkEventButton          *event,
108                                                           gpointer                 user_data);
109 static void     gtk_entry_completion_selection_changed   (GtkTreeSelection        *selection,
110                                                           gpointer                 data);
111
112 static void     gtk_entry_completion_insert_action       (GtkEntryCompletion      *completion,
113                                                           gint                     index,
114                                                           const gchar             *string,
115                                                           gboolean                 markup);
116 static void     gtk_entry_completion_action_data_func    (GtkTreeViewColumn       *tree_column,
117                                                           GtkCellRenderer         *cell,
118                                                           GtkTreeModel            *model,
119                                                           GtkTreeIter             *iter,
120                                                           gpointer                 data);
121
122
123 static GObjectClass *parent_class = NULL;
124 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
125
126
127 GType
128 gtk_entry_completion_get_type (void)
129 {
130   static GType entry_completion_type = 0;
131
132   if (!entry_completion_type)
133     {
134       static const GTypeInfo entry_completion_info =
135       {
136         sizeof (GtkEntryCompletionClass),
137         NULL,
138         NULL,
139         (GClassInitFunc) gtk_entry_completion_class_init,
140         NULL,
141         NULL,
142         sizeof (GtkEntryCompletion),
143         0,
144         (GInstanceInitFunc) gtk_entry_completion_init
145       };
146
147       static const GInterfaceInfo cell_layout_info =
148       {
149         (GInterfaceInitFunc) gtk_entry_completion_cell_layout_init,
150         NULL,
151         NULL
152       };
153
154       entry_completion_type =
155         g_type_register_static (G_TYPE_OBJECT, "GtkEntryCompletion",
156                                 &entry_completion_info, 0);
157
158       g_type_add_interface_static (entry_completion_type,
159                                    GTK_TYPE_CELL_LAYOUT,
160                                    &cell_layout_info);
161     }
162
163   return entry_completion_type;
164 }
165
166 static void
167 gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
168 {
169   GObjectClass *object_class;
170
171   parent_class = g_type_class_peek_parent (klass);
172   object_class = (GObjectClass *)klass;
173
174   object_class->set_property = gtk_entry_completion_set_property;
175   object_class->get_property = gtk_entry_completion_get_property;
176   object_class->finalize = gtk_entry_completion_finalize;
177
178   /**
179    * GtkEntryCompletion::match-selected:
180    * @widget: the object which received the signal
181    * @model: the #GtkTreeModel containing the matches
182    * @iter: a #GtkTreeIter positioned at the selected match
183    * 
184    * The ::match-selected signal is emitted when a match from the list
185    * is selected. The default behaviour is to replace the contents of the
186    * entry with the contents of the text column in the row pointed to by
187    * @iter.
188    *
189    * Return value: %TRUE if the signal has been handled
190    */ 
191   entry_completion_signals[MATCH_SELECTED] =
192     g_signal_new ("match_selected",
193                   G_TYPE_FROM_CLASS (klass),
194                   G_SIGNAL_RUN_LAST,
195                   G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
196                   _gtk_boolean_handled_accumulator, NULL,
197                   _gtk_marshal_BOOLEAN__OBJECT_BOXED,
198                   G_TYPE_BOOLEAN, 2,
199                   GTK_TYPE_TREE_MODEL,
200                   GTK_TYPE_TREE_ITER);
201                   
202   /**
203    * GtkEntryCompletion::action-activated:
204    * @widget: the object which received the signal
205    * @index: the index of the activated action
206    *
207    * The ::action-activated signal is emitted when an action
208    * is activated.
209    */
210   entry_completion_signals[ACTION_ACTIVATED] =
211     g_signal_new ("action_activated",
212                   G_TYPE_FROM_CLASS (klass),
213                   G_SIGNAL_RUN_LAST,
214                   G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
215                   NULL, NULL,
216                   _gtk_marshal_NONE__INT,
217                   G_TYPE_NONE, 1,
218                   G_TYPE_INT);
219
220   g_object_class_install_property (object_class,
221                                    PROP_MODEL,
222                                    g_param_spec_object ("model",
223                                                         P_("Completion Model"),
224                                                         P_("The model to find matches in"),
225                                                         GTK_TYPE_TREE_MODEL,
226                                                         G_PARAM_READWRITE));
227   g_object_class_install_property (object_class,
228                                    PROP_MINIMUM_KEY_LENGTH,
229                                    g_param_spec_int ("minimum_key_length",
230                                                      P_("Minimum Key Length"),
231                                                      P_("Minimum length of the search key in order to look up matches"),
232                                                      -1,
233                                                      G_MAXINT,
234                                                      1,
235                                                      G_PARAM_READWRITE));
236
237   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
238 }
239
240 static void
241 gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
242 {
243   iface->pack_start = gtk_entry_completion_pack_start;
244   iface->pack_end = gtk_entry_completion_pack_end;
245   iface->clear = gtk_entry_completion_clear;
246   iface->add_attribute = gtk_entry_completion_add_attribute;
247   iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
248   iface->clear_attributes = gtk_entry_completion_clear_attributes;
249   iface->reorder = gtk_entry_completion_reorder;
250 }
251
252 static void
253 gtk_entry_completion_init (GtkEntryCompletion *completion)
254 {
255   GtkCellRenderer *cell;
256   GtkTreeSelection *sel;
257   GtkEntryCompletionPrivate *priv;
258   GtkWidget *popup_frame;
259
260   /* yes, also priv, need to keep the code readable */
261   priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
262
263   priv->minimum_key_length = 1;
264   priv->text_column = -1;
265
266   /* completions */
267   priv->filter_model = NULL;
268
269   priv->tree_view = gtk_tree_view_new ();
270   g_signal_connect (priv->tree_view, "button_press_event",
271                     G_CALLBACK (gtk_entry_completion_list_button_press),
272                     completion);
273   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
274
275   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
276   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
277   gtk_tree_selection_unselect_all (sel);
278   g_signal_connect (sel, "changed",
279                     G_CALLBACK (gtk_entry_completion_selection_changed),
280                     completion);
281   priv->first_sel_changed = TRUE;
282
283   priv->column = gtk_tree_view_column_new ();
284   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
285
286   priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
287   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
288                                   GTK_POLICY_NEVER,
289                                   GTK_POLICY_AUTOMATIC);
290   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
291                                        GTK_SHADOW_NONE);
292
293   /* a nasty hack to get the completions treeview to size nicely */
294   gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
295
296   /* actions */
297   priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
298
299   priv->action_view =
300     gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
301   g_signal_connect (priv->action_view, "button_press_event",
302                     G_CALLBACK (gtk_entry_completion_action_button_press),
303                     completion);
304   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
305
306   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
307   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
308   gtk_tree_selection_unselect_all (sel);
309
310   cell = gtk_cell_renderer_text_new ();
311   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
312                                               0, "",
313                                               cell,
314                                               gtk_entry_completion_action_data_func,
315                                               NULL,
316                                               NULL);
317
318   /* pack it all */
319   priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
320   gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
321   g_signal_connect (priv->popup_window, "key_press_event",
322                     G_CALLBACK (gtk_entry_completion_popup_key_press),
323                     completion);
324   g_signal_connect (priv->popup_window, "button_press_event",
325                     G_CALLBACK (gtk_entry_completion_popup_button_press),
326                     completion);
327
328   popup_frame = gtk_frame_new (NULL);
329   gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
330                              GTK_SHADOW_ETCHED_IN);
331   gtk_widget_show (popup_frame);
332   gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
333   
334   priv->vbox = gtk_vbox_new (FALSE, 0);
335   gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
336
337   gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
338   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
339                       TRUE, TRUE, 0);
340
341   /* we don't want to see the action treeview when no actions have
342    * been inserted, so we pack the action treeview after the first
343    * action has been added
344    */
345 }
346
347 static void
348 gtk_entry_completion_set_property (GObject      *object,
349                                    guint         prop_id,
350                                    const GValue *value,
351                                    GParamSpec   *pspec)
352 {
353   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
354
355   switch (prop_id)
356     {
357       case PROP_MODEL:
358         gtk_entry_completion_set_model (completion,
359                                         g_value_get_object (value));
360         break;
361
362       case PROP_MINIMUM_KEY_LENGTH:
363         gtk_entry_completion_set_minimum_key_length (completion,
364                                                      g_value_get_int (value));
365         break;
366
367       default:
368         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
369         break;
370     }
371 }
372
373 static void
374 gtk_entry_completion_get_property (GObject    *object,
375                                    guint       prop_id,
376                                    GValue     *value,
377                                    GParamSpec *pspec)
378 {
379   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
380
381   switch (prop_id)
382     {
383       case PROP_MODEL:
384         g_value_set_object (value,
385                             gtk_entry_completion_get_model (completion));
386         break;
387
388       case PROP_MINIMUM_KEY_LENGTH:
389         g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
390         break;
391
392       default:
393         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
394         break;
395     }
396 }
397
398 static void
399 gtk_entry_completion_finalize (GObject *object)
400 {
401   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
402
403   if (completion->priv->tree_view)
404     gtk_widget_destroy (completion->priv->tree_view);
405
406   if (completion->priv->entry)
407     gtk_entry_set_completion (GTK_ENTRY (completion->priv->entry), NULL);
408
409   if (completion->priv->actions)
410     g_object_unref (completion->priv->actions);
411
412   if (completion->priv->case_normalized_key)
413     g_free (completion->priv->case_normalized_key);
414
415   if (completion->priv->popup_window)
416     gtk_widget_destroy (completion->priv->popup_window);
417
418   G_OBJECT_CLASS (parent_class)->finalize (object);
419 }
420
421 /* implement cell layout interface */
422 static void
423 gtk_entry_completion_pack_start (GtkCellLayout   *cell_layout,
424                                  GtkCellRenderer *cell,
425                                  gboolean         expand)
426 {
427   GtkEntryCompletionPrivate *priv;
428
429   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
430
431   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
432
433   gtk_tree_view_column_pack_start (priv->column, cell, expand);
434 }
435
436 static void
437 gtk_entry_completion_pack_end (GtkCellLayout   *cell_layout,
438                                GtkCellRenderer *cell,
439                                gboolean         expand)
440 {
441   GtkEntryCompletionPrivate *priv;
442
443   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
444
445   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
446
447   gtk_tree_view_column_pack_end (priv->column, cell, expand);
448 }
449
450 static void
451 gtk_entry_completion_clear (GtkCellLayout *cell_layout)
452 {
453   GtkEntryCompletionPrivate *priv;
454
455   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
456
457   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
458
459   gtk_tree_view_column_clear (priv->column);
460 }
461
462 static void
463 gtk_entry_completion_add_attribute (GtkCellLayout   *cell_layout,
464                                     GtkCellRenderer *cell,
465                                     const gchar     *attribute,
466                                     gint             column)
467 {
468   GtkEntryCompletionPrivate *priv;
469
470   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
471
472   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
473
474   gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
475 }
476
477 static void
478 gtk_entry_completion_set_cell_data_func (GtkCellLayout          *cell_layout,
479                                          GtkCellRenderer        *cell,
480                                          GtkCellLayoutDataFunc   func,
481                                          gpointer                func_data,
482                                          GDestroyNotify          destroy)
483 {
484   GtkEntryCompletionPrivate *priv;
485
486   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
487
488   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
489
490   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
491                                       cell, func, func_data, destroy);
492 }
493
494 static void
495 gtk_entry_completion_clear_attributes (GtkCellLayout   *cell_layout,
496                                        GtkCellRenderer *cell)
497 {
498   GtkEntryCompletionPrivate *priv;
499
500   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
501
502   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
503
504   gtk_tree_view_column_clear_attributes (priv->column, cell);
505 }
506
507 static void
508 gtk_entry_completion_reorder (GtkCellLayout   *cell_layout,
509                               GtkCellRenderer *cell,
510                               gint             position)
511 {
512   GtkEntryCompletionPrivate *priv;
513
514   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
515
516   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
517
518   gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
519 }
520
521 /* all those callbacks */
522 static gboolean
523 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
524                                               const gchar        *key,
525                                               GtkTreeIter        *iter,
526                                               gpointer            user_data)
527 {
528   gchar *item = NULL;
529   gchar *normalized_string;
530   gchar *case_normalized_string;
531
532   gboolean ret = FALSE;
533
534   GtkTreeModel *model;
535
536   model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
537
538   gtk_tree_model_get (model, iter,
539                       completion->priv->text_column, &item,
540                       -1);
541
542   if (item != NULL)
543     {
544       normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
545       case_normalized_string = g_utf8_casefold (normalized_string, -1);
546       
547       if (!strncmp (key, case_normalized_string, strlen (key)))
548         ret = TRUE;
549       
550       g_free (item);
551       g_free (normalized_string);
552       g_free (case_normalized_string);
553     }
554
555   return ret;
556 }
557
558 static gboolean
559 gtk_entry_completion_visible_func (GtkTreeModel *model,
560                                    GtkTreeIter  *iter,
561                                    gpointer      data)
562 {
563   gboolean ret = FALSE;
564
565   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
566
567   if (!completion->priv->case_normalized_key)
568     return ret;
569
570   if (completion->priv->match_func)
571     ret = (* completion->priv->match_func) (completion,
572                                             completion->priv->case_normalized_key,
573                                             iter,
574                                             completion->priv->match_data);
575   else if (completion->priv->text_column >= 0)
576     ret = gtk_entry_completion_default_completion_func (completion,
577                                                         completion->priv->case_normalized_key,
578                                                         iter,
579                                                         NULL);
580
581   return ret;
582 }
583
584 static gboolean
585 gtk_entry_completion_popup_key_press (GtkWidget   *widget,
586                                       GdkEventKey *event,
587                                       gpointer     user_data)
588 {
589   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
590
591   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
592     return FALSE;
593
594   /* propagate event to the entry */
595   gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
596
597   return TRUE;
598 }
599
600 static gboolean
601 gtk_entry_completion_popup_button_press (GtkWidget      *widget,
602                                          GdkEventButton *event,
603                                          gpointer        user_data)
604 {
605   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
606
607   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
608     return FALSE;
609
610   /* if we come here, it's usually time to popdown */
611   _gtk_entry_completion_popdown (completion);
612
613   return TRUE;
614 }
615
616 static gboolean
617 gtk_entry_completion_list_button_press (GtkWidget      *widget,
618                                         GdkEventButton *event,
619                                         gpointer        user_data)
620 {
621   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
622   GtkTreePath *path = NULL;
623
624   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
625     return FALSE;
626
627   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
628                                      event->x, event->y,
629                                      &path, NULL, NULL, NULL))
630     {
631       gboolean entry_set;
632       GtkTreeIter iter;
633
634       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
635                                &iter, path);
636       gtk_tree_path_free (path);
637
638       g_signal_handler_block (completion->priv->entry,
639                               completion->priv->changed_id);
640       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
641                      0, GTK_TREE_MODEL (completion->priv->filter_model),
642                      &iter, &entry_set);
643       g_signal_handler_unblock (completion->priv->entry,
644                                 completion->priv->changed_id);
645
646       if (!entry_set)
647         {
648           gchar *str = NULL;
649
650           gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
651                               &iter,
652                               completion->priv->text_column, &str,
653                               -1);
654
655           g_signal_handler_block (completion->priv->entry,
656                                   completion->priv->changed_id);
657           gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
658           g_signal_handler_unblock (completion->priv->entry,
659                                     completion->priv->changed_id);
660
661           /* move cursor to the end */
662           gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry),
663                                      -1);
664
665           g_free (str);
666         }
667
668       _gtk_entry_completion_popdown (completion);
669       return TRUE;
670     }
671
672   return FALSE;
673 }
674
675 static gboolean
676 gtk_entry_completion_action_button_press (GtkWidget      *widget,
677                                           GdkEventButton *event,
678                                           gpointer        user_data)
679 {
680   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
681   GtkTreePath *path = NULL;
682
683   if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
684     return FALSE;
685
686   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
687                                      event->x, event->y,
688                                      &path, NULL, NULL, NULL))
689     {
690       g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
691                      0, gtk_tree_path_get_indices (path)[0]);
692       gtk_tree_path_free (path);
693
694       _gtk_entry_completion_popdown (completion);
695       return TRUE;
696     }
697
698   return FALSE;
699 }
700
701 static void
702 gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
703                                        GtkCellRenderer   *cell,
704                                        GtkTreeModel      *model,
705                                        GtkTreeIter       *iter,
706                                        gpointer           data)
707 {
708   gchar *string = NULL;
709   gboolean markup;
710
711   gtk_tree_model_get (model, iter,
712                       0, &string,
713                       1, &markup,
714                       -1);
715
716   if (!string)
717     return;
718
719   if (markup)
720     g_object_set (G_OBJECT (cell),
721                   "text", NULL,
722                   "markup", string,
723                   NULL);
724   else
725     g_object_set (G_OBJECT (cell),
726                   "markup", NULL,
727                   "text", string,
728                   NULL);
729
730   g_free (string);
731 }
732
733 static void
734 gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
735                                         gpointer          data)
736 {
737   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
738
739   if (completion->priv->first_sel_changed)
740     {
741       completion->priv->first_sel_changed = FALSE;
742       if (gtk_widget_is_focus (completion->priv->tree_view))
743         gtk_tree_selection_unselect_all (selection);
744     }
745 }
746
747 /* public API */
748
749 /**
750  * gtk_entry_completion_new:
751  *
752  * Creates a new #GtkEntryCompletion object.
753  *
754  * Return value: A newly created #GtkEntryCompletion object.
755  *
756  * Since: 2.4
757  */
758 GtkEntryCompletion *
759 gtk_entry_completion_new (void)
760 {
761   GtkEntryCompletion *completion;
762
763   completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
764
765   return completion;
766 }
767
768 /**
769  * gtk_entry_completion_get_entry:
770  * @completion: A #GtkEntryCompletion.
771  *
772  * Gets the entry @completion has been attached to.
773  *
774  * Return value: The entry @completion has been attached to.
775  *
776  * Since: 2.4
777  */
778 GtkWidget *
779 gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
780 {
781   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
782
783   return completion->priv->entry;
784 }
785
786 /**
787  * gtk_entry_completion_set_model:
788  * @completion: A #GtkEntryCompletion.
789  * @model: The #GtkTreeModel.
790  *
791  * Sets the model for a #GtkEntryCompletion. If @completion already has
792  * a model set, it will remove it before setting the new model.
793  *
794  * Since: 2.4
795  */
796 void
797 gtk_entry_completion_set_model (GtkEntryCompletion *completion,
798                                 GtkTreeModel       *model)
799 {
800   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
801   g_return_if_fail (GTK_IS_TREE_MODEL (model));
802
803   /* code will unref the old filter model (if any) */
804   completion->priv->filter_model =
805     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
806   gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
807                                           gtk_entry_completion_visible_func,
808                                           completion,
809                                           NULL);
810   gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
811                            GTK_TREE_MODEL (completion->priv->filter_model));
812   g_object_unref (G_OBJECT (completion->priv->filter_model));
813 }
814
815 /**
816  * gtk_entry_completion_get_model:
817  * @completion: A #GtkEntryCompletion.
818  *
819  * Returns the model the #GtkEntryCompletion is using as data source.
820  * Returns %NULL if the model is unset.
821  *
822  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
823  *
824  * Since: 2.4
825  */
826 GtkTreeModel *
827 gtk_entry_completion_get_model (GtkEntryCompletion *completion)
828 {
829   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
830
831   return gtk_tree_model_filter_get_model (completion->priv->filter_model);
832 }
833
834 /**
835  * gtk_entry_completion_set_match_func:
836  * @completion: A #GtkEntryCompletion.
837  * @func: The #GtkEntryCompletionMatchFunc to use.
838  * @func_data: The user data for @func.
839  * @func_notify: Destroy notifier for @func_data.
840  *
841  * Sets the match function for @completion to be @func. The match function
842  * is used to determine if a row should or should not be in the completion
843  * list.
844  *
845  * Since: 2.4.
846  */
847 void
848 gtk_entry_completion_set_match_func (GtkEntryCompletion          *completion,
849                                      GtkEntryCompletionMatchFunc  func,
850                                      gpointer                     func_data,
851                                      GDestroyNotify               func_notify)
852 {
853   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
854
855   if (completion->priv->match_notify)
856     (* completion->priv->match_notify) (completion->priv->match_data);
857
858   completion->priv->match_func = func;
859   completion->priv->match_data = func_data;
860   completion->priv->match_notify = func_notify;
861 }
862
863 /**
864  * gtk_entry_completion_set_minimum_key_length:
865  * @completion: A #GtkEntryCompletion.
866  * @length: The minimum length of the key in order to start completing.
867  *
868  * Requires the length of the search key for @completion to be at least
869  * @length. This is useful for long lists, where completing using a small
870  * key takes a lot of time and will come up with meaningless results anyway
871  * (ie, a too large dataset).
872  *
873  * Since: 2.4
874  */
875 void
876 gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
877                                              gint                length)
878 {
879   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
880   g_return_if_fail (length >= 1);
881
882   completion->priv->minimum_key_length = length;
883 }
884
885 /**
886  * gtk_entry_completion_get_minimum_key_length:
887  * @completion: A #GtkEntryCompletion.
888  *
889  * Returns the minimum key length as set for @completion.
890  *
891  * Return value: The currently used minimum key length.
892  *
893  * Since: 2.4
894  */
895 gint
896 gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
897 {
898   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
899
900   return completion->priv->minimum_key_length;
901 }
902
903 /**
904  * gtk_entry_completion_complete:
905  * @completion: A #GtkEntryCompletion.
906  *
907  * Requests a completion operation, or in other words a refiltering of the
908  * current list with completions, using the current key. The completion list
909  * view will be updated accordingly.
910  *
911  * Since: 2.4
912  */
913 void
914 gtk_entry_completion_complete (GtkEntryCompletion *completion)
915 {
916   gchar *tmp;
917
918   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
919   g_return_if_fail (completion->priv->filter_model != NULL);
920
921   if (completion->priv->case_normalized_key)
922     g_free (completion->priv->case_normalized_key);
923
924   tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
925                           -1, G_NORMALIZE_ALL);
926   completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
927   g_free (tmp);
928
929   gtk_tree_model_filter_refilter (completion->priv->filter_model);
930 }
931
932 static void
933 gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
934                                     gint                index,
935                                     const gchar        *string,
936                                     gboolean            markup)
937 {
938   GtkTreeIter iter;
939
940   gtk_list_store_insert (completion->priv->actions, &iter, index);
941   gtk_list_store_set (completion->priv->actions, &iter,
942                       0, string,
943                       1, markup,
944                       -1);
945
946   if (!completion->priv->action_view->parent)
947     {
948       GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
949
950       gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
951                                 path, NULL, FALSE);
952       gtk_tree_path_free (path);
953
954       gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
955                           completion->priv->action_view, FALSE, FALSE, 0);
956       gtk_widget_show (completion->priv->action_view);
957     }
958 }
959
960 /**
961  * gtk_entry_completion_insert_action_text:
962  * @completion: A #GtkEntryCompletion.
963  * @index: The index of the item to insert.
964  * @text: Text of the item to insert.
965  *
966  * Inserts an action in @completion's action item list at position @index
967  * with text @text. If you want the action item to have markup, use
968  * gtk_entry_completion_insert_action_markup().
969  *
970  * Since: 2.4
971  */
972 void
973 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
974                                          gint                index,
975                                          const gchar        *text)
976 {
977   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
978   g_return_if_fail (text != NULL);
979
980   gtk_entry_completion_insert_action (completion, index, text, FALSE);
981 }
982
983 /**
984  * gtk_entry_completion_insert_action_markup:
985  * @completion: A #GtkEntryCompletion.
986  * @index: The index of the item to insert.
987  * @markup: Markup of the item to insert.
988  *
989  * Inserts an action in @completion's action item list at position @index
990  * with markup @markup.
991  *
992  * Since: 2.4
993  */
994 void
995 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
996                                            gint                index,
997                                            const gchar        *markup)
998 {
999   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1000   g_return_if_fail (markup != NULL);
1001
1002   gtk_entry_completion_insert_action (completion, index, markup, TRUE);
1003 }
1004
1005 /**
1006  * gtk_entry_completion_delete_action:
1007  * @completion: A #GtkEntryCompletion.
1008  * @index: The index of the item to Delete.
1009  *
1010  * Deletes the action at @index from @completion's action list.
1011  *
1012  * Since: 2.4
1013  */
1014 void
1015 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
1016                                     gint                index)
1017 {
1018   GtkTreeIter iter;
1019
1020   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1021   g_return_if_fail (index >= 0);
1022
1023   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
1024                                  &iter, NULL, index);
1025   gtk_list_store_remove (completion->priv->actions, &iter);
1026 }
1027
1028 /**
1029  * gtk_entry_completion_set_text_column:
1030  * @completion: A #GtkEntryCompletion.
1031  * @column: The column in the model of @completion to get strings from.
1032  *
1033  * Convenience function for setting up the most used case of this code: a
1034  * completion list with just strings. This function will set up @completion
1035  * to have a list displaying all (and just) strings in the completion list,
1036  * and to get those strings from @column in the model of @completion.
1037  *
1038  * This functions creates and adds a GtkCellRendererText for the selected column.
1039
1040  * Since: 2.4
1041  */
1042 void
1043 gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
1044                                       gint                column)
1045 {
1046   GtkCellRenderer *cell;
1047
1048   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1049   g_return_if_fail (column >= 0);
1050
1051   completion->priv->text_column = column;
1052
1053   cell = gtk_cell_renderer_text_new ();
1054   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1055                               cell, TRUE);
1056   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1057                                  cell,
1058                                  "text", column);
1059 }
1060
1061 /* private */
1062
1063 /* lame copy from gtkentry.c */
1064 static void
1065 get_borders (GtkEntry *entry,
1066              gint     *xborder,
1067              gint     *yborder)
1068 {
1069   GtkWidget *widget = GTK_WIDGET (entry);
1070   gint focus_width;
1071   gboolean interior_focus;
1072
1073   gtk_widget_style_get (widget,
1074                         "interior-focus", &interior_focus,
1075                         "focus-line-width", &focus_width,
1076                         NULL);
1077
1078   if (entry->has_frame)
1079     {
1080       *xborder = widget->style->xthickness;
1081       *yborder = widget->style->ythickness;
1082     }
1083   else
1084     {
1085       *xborder = 0;
1086       *yborder = 0;
1087     }
1088
1089   if (!interior_focus)
1090     {
1091       *xborder += focus_width;
1092       *yborder += focus_width;
1093     }
1094 }
1095
1096 /* some nasty size requisition */
1097 gboolean
1098 _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
1099 {
1100   gint x, y;
1101   gint items, height, x_border, y_border;
1102   GdkScreen *screen;
1103   gint monitor_num;
1104   GdkRectangle monitor;
1105   GtkRequisition popup_req;
1106   GtkTreePath *path;
1107   gboolean above;
1108
1109   gdk_window_get_origin (completion->priv->entry->window, &x, &y);
1110   get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
1111
1112   x += x_border;
1113   y += 2 * y_border;
1114
1115   items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
1116
1117   items = MIN (items, 15);
1118
1119   gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
1120                                       NULL, NULL, NULL, &height);
1121
1122   if (items <= 0)
1123     gtk_widget_hide (completion->priv->scrolled_window);
1124   else
1125     gtk_widget_show (completion->priv->scrolled_window);
1126
1127   gtk_widget_set_size_request (completion->priv->tree_view,
1128                                completion->priv->entry->allocation.width - 2 * x_border,
1129                                items * height);
1130
1131   /* default on no match */
1132   completion->priv->current_selected = -1;
1133
1134   items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
1135
1136   if (items)
1137     {
1138       gtk_widget_show (completion->priv->action_view);
1139
1140       gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0),
1141                                           NULL, NULL, NULL, NULL,
1142                                           &height);
1143
1144       gtk_widget_set_size_request (completion->priv->action_view,
1145                                    completion->priv->entry->allocation.width - 2 * x_border,
1146                                    items * height);
1147     }
1148   else
1149     gtk_widget_hide (completion->priv->action_view);
1150
1151   gtk_widget_size_request (completion->priv->popup_window, &popup_req);
1152
1153   screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
1154   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1155                                                   GTK_WIDGET (completion->priv->entry)->window);
1156   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1157   
1158   if (x < monitor.x)
1159     x = monitor.x;
1160   else if (x + popup_req.width > monitor.x + monitor.width)
1161     x = monitor.x + monitor.width - popup_req.width;
1162   
1163   if (y + height + popup_req.height <= monitor.y + monitor.height)
1164     {
1165       y += height;
1166       above = FALSE;
1167       path = gtk_tree_path_new_from_indices (0, -1);
1168     }
1169   else
1170     {
1171       y -= popup_req.height;
1172       above = TRUE;
1173       path = gtk_tree_path_new_from_indices (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL) - 1, -1);
1174     }
1175   
1176   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path, NULL, FALSE, 0.0, 0.0);
1177   gtk_tree_path_free (path);
1178
1179   gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
1180
1181   return above;
1182 }
1183
1184 void
1185 _gtk_entry_completion_popup (GtkEntryCompletion *completion)
1186 {
1187   GtkTreeViewColumn *column;
1188   GList *renderers;
1189
1190   if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
1191     return;
1192
1193   completion->priv->may_wrap = TRUE;
1194
1195   column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1196   renderers = gtk_tree_view_column_get_cell_renderers (column);
1197   gtk_widget_ensure_style (completion->priv->tree_view);
1198   g_object_set (GTK_CELL_RENDERER (renderers->data), "cell_background_gdk",
1199                 &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
1200                 NULL);
1201   g_list_free (renderers);
1202
1203   gtk_widget_show_all (completion->priv->vbox);
1204
1205   _gtk_entry_completion_resize_popup (completion);
1206
1207   gtk_widget_show (completion->priv->popup_window);
1208
1209   gtk_grab_add (completion->priv->popup_window);
1210   gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
1211                     GDK_BUTTON_PRESS_MASK |
1212                     GDK_BUTTON_RELEASE_MASK |
1213                     GDK_POINTER_MOTION_MASK,
1214                     NULL, NULL, GDK_CURRENT_TIME);
1215 }
1216
1217 void
1218 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
1219 {
1220   gdk_pointer_ungrab (GDK_CURRENT_TIME);
1221   gtk_grab_remove (completion->priv->popup_window);
1222
1223   gtk_widget_hide (completion->priv->popup_window);
1224 }