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