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