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