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