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 "gtkmarshalers.h"
36 
37 #include "gtkprivate.h"
38 #include "gtkalias.h"
39 
40 #include <string.h>
41 
42 
43 /* signals */
44 enum
45 {
46   INSERT_PREFIX,
47   MATCH_SELECTED,
48   ACTION_ACTIVATED,
49   CURSOR_ON_MATCH,
50   LAST_SIGNAL
51 };
52 
53 /* properties */
54 enum
55 {
56   PROP_0,
57   PROP_MODEL,
58   PROP_MINIMUM_KEY_LENGTH,
59   PROP_TEXT_COLUMN,
60   PROP_INLINE_COMPLETION,
61   PROP_POPUP_COMPLETION,
62   PROP_POPUP_SET_WIDTH,
63   PROP_POPUP_SINGLE_MATCH,
64   PROP_INLINE_SELECTION
65 };
66 
67 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
68 
69 static void                                                             gtk_entry_completion_cell_layout_init    (GtkCellLayoutIface      *iface);
70 static void     gtk_entry_completion_set_property        (GObject      *object,
71                                                           guint         prop_id,
72                                                           const GValue *value,
73                                                           GParamSpec   *pspec);
74 static void     gtk_entry_completion_get_property        (GObject      *object,
75                                                           guint         prop_id,
76                                                           GValue       *value,
77                                                           GParamSpec   *pspec);
78 static void                                                             gtk_entry_completion_finalize            (GObject                 *object);
79 
80 static void     gtk_entry_completion_pack_start          (GtkCellLayout         *cell_layout,
81                                                           GtkCellRenderer       *cell,
82                                                           gboolean               expand);
83 static void     gtk_entry_completion_pack_end            (GtkCellLayout         *cell_layout,
84                                                           GtkCellRenderer       *cell,
85                                                           gboolean               expand);
86 static void                                                                      gtk_entry_completion_clear               (GtkCellLayout           *cell_layout);
87 static void     gtk_entry_completion_add_attribute       (GtkCellLayout         *cell_layout,
88                                                           GtkCellRenderer       *cell,
89                                                           const char            *attribute,
90                                                           gint                   column);
91 static void     gtk_entry_completion_set_cell_data_func  (GtkCellLayout         *cell_layout,
92                                                           GtkCellRenderer       *cell,
93                                                           GtkCellLayoutDataFunc  func,
94                                                           gpointer               func_data,
95                                                           GDestroyNotify         destroy);
96 static void     gtk_entry_completion_clear_attributes    (GtkCellLayout         *cell_layout,
97                                                           GtkCellRenderer       *cell);
98 static void     gtk_entry_completion_reorder             (GtkCellLayout         *cell_layout,
99                                                           GtkCellRenderer       *cell,
100                                                           gint                   position);
101 static GList *  gtk_entry_completion_get_cells           (GtkCellLayout         *cell_layout);
102 
103 static gboolean gtk_entry_completion_visible_func        (GtkTreeModel       *model,
104                                                           GtkTreeIter        *iter,
105                                                           gpointer            data);
106 static gboolean gtk_entry_completion_popup_key_event     (GtkWidget          *widget,
107                                                           GdkEventKey        *event,
108                                                           gpointer            user_data);
109 static gboolean gtk_entry_completion_popup_button_press  (GtkWidget          *widget,
110                                                           GdkEventButton     *event,
111                                                           gpointer            user_data);
112 static gboolean gtk_entry_completion_list_button_press   (GtkWidget          *widget,
113                                                           GdkEventButton     *event,
114                                                           gpointer            user_data);
115 static gboolean gtk_entry_completion_action_button_press (GtkWidget          *widget,
116                                                           GdkEventButton     *event,
117                                                           gpointer            user_data);
118 static void     gtk_entry_completion_selection_changed   (GtkTreeSelection   *selection,
119                                                           gpointer            data);
120 static gboolean	gtk_entry_completion_list_enter_notify	 (GtkWidget          *widget,
121 							  GdkEventCrossing   *event,
122 							  gpointer            data);
123 static gboolean gtk_entry_completion_list_motion_notify	 (GtkWidget	     *widget,
124 							  GdkEventMotion     *event,
125 							  gpointer 	      data);
126 static void     gtk_entry_completion_insert_action       (GtkEntryCompletion *completion,
127                                                           gint                index,
128                                                           const gchar        *string,
129                                                           gboolean            markup);
130 static void     gtk_entry_completion_action_data_func    (GtkTreeViewColumn  *tree_column,
131                                                           GtkCellRenderer    *cell,
132                                                           GtkTreeModel       *model,
133                                                           GtkTreeIter        *iter,
134                                                           gpointer            data);
135 
136 static gboolean gtk_entry_completion_match_selected      (GtkEntryCompletion *completion,
137 							  GtkTreeModel       *model,
138 							  GtkTreeIter        *iter);
139 static gboolean gtk_entry_completion_real_insert_prefix  (GtkEntryCompletion *completion,
140 							  const gchar        *prefix);
141 static gboolean gtk_entry_completion_cursor_on_match     (GtkEntryCompletion *completion,
142 							  GtkTreeModel       *model,
143 							  GtkTreeIter        *iter);
144 static gboolean gtk_entry_completion_insert_completion   (GtkEntryCompletion *completion,
145                                                           GtkTreeModel       *model,
146                                                           GtkTreeIter        *iter);
147 static void     gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
148                                                              const gchar *text);
149 
150 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
151 
152 /* GtkBuildable */
153 static void     gtk_entry_completion_buildable_init      (GtkBuildableIface  *iface);
154 
G_DEFINE_TYPE_WITH_CODE(GtkEntryCompletion,gtk_entry_completion,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,gtk_entry_completion_cell_layout_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_entry_completion_buildable_init))155 G_DEFINE_TYPE_WITH_CODE (GtkEntryCompletion, gtk_entry_completion, G_TYPE_OBJECT,
156 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
157 						gtk_entry_completion_cell_layout_init)
158 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
159 						gtk_entry_completion_buildable_init))
160 
161 
162 static void
163 gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
164 {
165   GObjectClass *object_class;
166 
167   object_class = (GObjectClass *)klass;
168 
169   object_class->set_property = gtk_entry_completion_set_property;
170   object_class->get_property = gtk_entry_completion_get_property;
171   object_class->finalize = gtk_entry_completion_finalize;
172 
173   klass->match_selected = gtk_entry_completion_match_selected;
174   klass->insert_prefix = gtk_entry_completion_real_insert_prefix;
175   klass->cursor_on_match = gtk_entry_completion_cursor_on_match;
176 
177   /**
178    * GtkEntryCompletion::insert-prefix:
179    * @widget: the object which received the signal
180    * @prefix: the common prefix of all possible completions
181    *
182    * Gets emitted when the inline autocompletion is triggered.
183    * The default behaviour is to make the entry display the
184    * whole prefix and select the newly inserted part.
185    *
186    * Applications may connect to this signal in order to insert only a
187    * smaller part of the @prefix into the entry - e.g. the entry used in
188    * the #GtkFileChooser inserts only the part of the prefix up to the
189    * next '/'.
190    *
191    * Return value: %TRUE if the signal has been handled
192    *
193    * Since: 2.6
194    */
195   entry_completion_signals[INSERT_PREFIX] =
196     g_signal_new (I_("insert-prefix"),
197                   G_TYPE_FROM_CLASS (klass),
198                   G_SIGNAL_RUN_LAST,
199                   G_STRUCT_OFFSET (GtkEntryCompletionClass, insert_prefix),
200                   _gtk_boolean_handled_accumulator, NULL,
201                   _gtk_marshal_BOOLEAN__STRING,
202                   G_TYPE_BOOLEAN, 1,
203                   G_TYPE_STRING);
204 
205   /**
206    * GtkEntryCompletion::match-selected:
207    * @widget: the object which received the signal
208    * @model: the #GtkTreeModel containing the matches
209    * @iter: a #GtkTreeIter positioned at the selected match
210    *
211    * Gets emitted when a match from the list is selected.
212    * The default behaviour is to replace the contents of the
213    * entry with the contents of the text column in the row
214    * pointed to by @iter.
215    *
216    * Return value: %TRUE if the signal has been handled
217    *
218    * Since: 2.4
219    */
220   entry_completion_signals[MATCH_SELECTED] =
221     g_signal_new (I_("match-selected"),
222                   G_TYPE_FROM_CLASS (klass),
223                   G_SIGNAL_RUN_LAST,
224                   G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
225                   _gtk_boolean_handled_accumulator, NULL,
226                   _gtk_marshal_BOOLEAN__OBJECT_BOXED,
227                   G_TYPE_BOOLEAN, 2,
228                   GTK_TYPE_TREE_MODEL,
229                   GTK_TYPE_TREE_ITER);
230 
231   /**
232    * GtkEntryCompletion::cursor-on-match:
233    * @widget: the object which received the signal
234    * @model: the #GtkTreeModel containing the matches
235    * @iter: a #GtkTreeIter positioned at the selected match
236    *
237    * Gets emitted when a match from the cursor is on a match
238    * of the list. The default behaviour is to replace the contents
239    * of the entry with the contents of the text column in the row
240    * pointed to by @iter.
241    *
242    * Return value: %TRUE if the signal has been handled
243    *
244    * Since: 2.12
245    */
246   entry_completion_signals[CURSOR_ON_MATCH] =
247     g_signal_new (I_("cursor-on-match"),
248 		  G_TYPE_FROM_CLASS (klass),
249 		  G_SIGNAL_RUN_LAST,
250 		  G_STRUCT_OFFSET (GtkEntryCompletionClass, cursor_on_match),
251 		  _gtk_boolean_handled_accumulator, NULL,
252 		  _gtk_marshal_BOOLEAN__OBJECT_BOXED,
253 		  G_TYPE_BOOLEAN, 2,
254 		  GTK_TYPE_TREE_MODEL,
255 		  GTK_TYPE_TREE_ITER);
256 
257   /**
258    * GtkEntryCompletion::action-activated:
259    * @widget: the object which received the signal
260    * @index: the index of the activated action
261    *
262    * Gets emitted when an action is activated.
263    *
264    * Since: 2.4
265    */
266   entry_completion_signals[ACTION_ACTIVATED] =
267     g_signal_new (I_("action-activated"),
268                   G_TYPE_FROM_CLASS (klass),
269                   G_SIGNAL_RUN_LAST,
270                   G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
271                   NULL, NULL,
272                   _gtk_marshal_VOID__INT,
273                   G_TYPE_NONE, 1,
274                   G_TYPE_INT);
275 
276   g_object_class_install_property (object_class,
277                                    PROP_MODEL,
278                                    g_param_spec_object ("model",
279                                                         P_("Completion Model"),
280                                                         P_("The model to find matches in"),
281                                                         GTK_TYPE_TREE_MODEL,
282                                                         GTK_PARAM_READWRITE));
283   g_object_class_install_property (object_class,
284                                    PROP_MINIMUM_KEY_LENGTH,
285                                    g_param_spec_int ("minimum-key-length",
286                                                      P_("Minimum Key Length"),
287                                                      P_("Minimum length of the search key in order to look up matches"),
288                                                      0,
289                                                      G_MAXINT,
290                                                      1,
291                                                      GTK_PARAM_READWRITE));
292   /**
293    * GtkEntryCompletion:text-column:
294    *
295    * The column of the model containing the strings.
296    * Note that the strings must be UTF-8.
297    *
298    * Since: 2.6
299    */
300   g_object_class_install_property (object_class,
301                                    PROP_TEXT_COLUMN,
302                                    g_param_spec_int ("text-column",
303                                                      P_("Text column"),
304                                                      P_("The column of the model containing the strings."),
305                                                      -1,
306                                                      G_MAXINT,
307                                                      -1,
308                                                      GTK_PARAM_READWRITE));
309 
310   /**
311    * GtkEntryCompletion:inline-completion:
312    *
313    * Determines whether the common prefix of the possible completions
314    * should be inserted automatically in the entry. Note that this
315    * requires text-column to be set, even if you are using a custom
316    * match function.
317    *
318    * Since: 2.6
319    **/
320   g_object_class_install_property (object_class,
321 				   PROP_INLINE_COMPLETION,
322 				   g_param_spec_boolean ("inline-completion",
323  							 P_("Inline completion"),
324  							 P_("Whether the common prefix should be inserted automatically"),
325  							 FALSE,
326  							 GTK_PARAM_READWRITE));
327   /**
328    * GtkEntryCompletion:popup-completion:
329    *
330    * Determines whether the possible completions should be
331    * shown in a popup window.
332    *
333    * Since: 2.6
334    **/
335   g_object_class_install_property (object_class,
336 				   PROP_POPUP_COMPLETION,
337 				   g_param_spec_boolean ("popup-completion",
338  							 P_("Popup completion"),
339  							 P_("Whether the completions should be shown in a popup window"),
340  							 TRUE,
341  							 GTK_PARAM_READWRITE));
342 
343   /**
344    * GtkEntryCompletion:popup-set-width:
345    *
346    * Determines whether the completions popup window will be
347    * resized to the width of the entry.
348    *
349    * Since: 2.8
350    */
351   g_object_class_install_property (object_class,
352 				   PROP_POPUP_SET_WIDTH,
353 				   g_param_spec_boolean ("popup-set-width",
354  							 P_("Popup set width"),
355  							 P_("If TRUE, the popup window will have the same size as the entry"),
356  							 TRUE,
357  							 GTK_PARAM_READWRITE));
358 
359   /**
360    * GtkEntryCompletion:popup-single-match:
361    *
362    * Determines whether the completions popup window will shown
363    * for a single possible completion. You probably want to set
364    * this to %FALSE if you are using
365    * <link linkend="GtkEntryCompletion--inline-completion">inline
366    * completion</link>.
367    *
368    * Since: 2.8
369    */
370   g_object_class_install_property (object_class,
371 				   PROP_POPUP_SINGLE_MATCH,
372 				   g_param_spec_boolean ("popup-single-match",
373  							 P_("Popup single match"),
374  							 P_("If TRUE, the popup window will appear for a single match."),
375  							 TRUE,
376  							 GTK_PARAM_READWRITE));
377   /**
378    * GtkEntryCompletion:inline-selection:
379    *
380    * Determines whether the possible completions on the popup
381    * will appear in the entry as you navigate through them.
382 
383    * Since: 2.12
384    */
385   g_object_class_install_property (object_class,
386 				   PROP_INLINE_SELECTION,
387 				   g_param_spec_boolean ("inline-selection",
388 							 P_("Inline selection"),
389 							 P_("Your description here"),
390 							 FALSE,
391 							 GTK_PARAM_READWRITE));
392 
393   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
394 }
395 
396 static void
gtk_entry_completion_buildable_init(GtkBuildableIface * iface)397 gtk_entry_completion_buildable_init (GtkBuildableIface *iface)
398 {
399   iface->add_child = _gtk_cell_layout_buildable_add_child;
400   iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
401   iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
402 }
403 
404 static void
gtk_entry_completion_cell_layout_init(GtkCellLayoutIface * iface)405 gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
406 {
407   iface->pack_start = gtk_entry_completion_pack_start;
408   iface->pack_end = gtk_entry_completion_pack_end;
409   iface->clear = gtk_entry_completion_clear;
410   iface->add_attribute = gtk_entry_completion_add_attribute;
411   iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
412   iface->clear_attributes = gtk_entry_completion_clear_attributes;
413   iface->reorder = gtk_entry_completion_reorder;
414   iface->get_cells = gtk_entry_completion_get_cells;
415 }
416 
417 static void
gtk_entry_completion_init(GtkEntryCompletion * completion)418 gtk_entry_completion_init (GtkEntryCompletion *completion)
419 {
420   GtkCellRenderer *cell;
421   GtkTreeSelection *sel;
422   GtkEntryCompletionPrivate *priv;
423   GtkWidget *popup_frame;
424 
425   /* yes, also priv, need to keep the code readable */
426   priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
427 
428   priv->minimum_key_length = 1;
429   priv->text_column = -1;
430   priv->has_completion = FALSE;
431   priv->inline_completion = FALSE;
432   priv->popup_completion = TRUE;
433   priv->popup_set_width = TRUE;
434   priv->popup_single_match = TRUE;
435   priv->inline_selection = FALSE;
436 
437   /* completions */
438   priv->filter_model = NULL;
439 
440   priv->tree_view = gtk_tree_view_new ();
441   g_signal_connect (priv->tree_view, "button-press-event",
442                     G_CALLBACK (gtk_entry_completion_list_button_press),
443                     completion);
444   g_signal_connect (priv->tree_view, "enter-notify-event",
445 		    G_CALLBACK (gtk_entry_completion_list_enter_notify),
446 		    completion);
447   g_signal_connect (priv->tree_view, "motion-notify-event",
448 		    G_CALLBACK (gtk_entry_completion_list_motion_notify),
449 		    completion);
450 
451   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
452   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
453 
454   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
455   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
456   gtk_tree_selection_unselect_all (sel);
457   g_signal_connect (sel, "changed",
458                     G_CALLBACK (gtk_entry_completion_selection_changed),
459                     completion);
460   priv->first_sel_changed = TRUE;
461 
462   priv->column = gtk_tree_view_column_new ();
463   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
464 
465   priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
466   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
467                                   GTK_POLICY_NEVER,
468                                   GTK_POLICY_AUTOMATIC);
469   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
470                                        GTK_SHADOW_NONE);
471 
472   /* a nasty hack to get the completions treeview to size nicely */
473   gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
474 
475   /* actions */
476   priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
477 
478   priv->action_view =
479     gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
480   g_object_ref_sink (priv->action_view);
481   g_signal_connect (priv->action_view, "button-press-event",
482                     G_CALLBACK (gtk_entry_completion_action_button_press),
483                     completion);
484   g_signal_connect (priv->action_view, "enter-notify-event",
485 		    G_CALLBACK (gtk_entry_completion_list_enter_notify),
486 		    completion);
487   g_signal_connect (priv->action_view, "motion-notify-event",
488 		    G_CALLBACK (gtk_entry_completion_list_motion_notify),
489 		    completion);
490   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
491   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->action_view), TRUE);
492 
493   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
494   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
495   gtk_tree_selection_unselect_all (sel);
496 
497   cell = gtk_cell_renderer_text_new ();
498   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
499                                               0, "",
500                                               cell,
501                                               gtk_entry_completion_action_data_func,
502                                               NULL,
503                                               NULL);
504 
505   /* pack it all */
506   priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
507   gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
508   gtk_window_set_type_hint (GTK_WINDOW(priv->popup_window),
509                             GDK_WINDOW_TYPE_HINT_COMBO);
510   g_signal_connect (priv->popup_window, "key-press-event",
511                     G_CALLBACK (gtk_entry_completion_popup_key_event),
512                     completion);
513   g_signal_connect (priv->popup_window, "key-release-event",
514                     G_CALLBACK (gtk_entry_completion_popup_key_event),
515                     completion);
516   g_signal_connect (priv->popup_window, "button-press-event",
517                     G_CALLBACK (gtk_entry_completion_popup_button_press),
518                     completion);
519 
520   popup_frame = gtk_frame_new (NULL);
521   gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
522 			     GTK_SHADOW_ETCHED_IN);
523   gtk_widget_show (popup_frame);
524   gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
525 
526   priv->vbox = gtk_vbox_new (FALSE, 0);
527   gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
528 
529   gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
530   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
531                       TRUE, TRUE, 0);
532 
533   /* we don't want to see the action treeview when no actions have
534    * been inserted, so we pack the action treeview after the first
535    * action has been added
536    */
537 }
538 
539 static void
gtk_entry_completion_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)540 gtk_entry_completion_set_property (GObject      *object,
541                                    guint         prop_id,
542                                    const GValue *value,
543                                    GParamSpec   *pspec)
544 {
545   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
546   GtkEntryCompletionPrivate *priv = completion->priv;
547 
548   switch (prop_id)
549     {
550       case PROP_MODEL:
551         gtk_entry_completion_set_model (completion,
552                                         g_value_get_object (value));
553         break;
554 
555       case PROP_MINIMUM_KEY_LENGTH:
556         gtk_entry_completion_set_minimum_key_length (completion,
557                                                      g_value_get_int (value));
558         break;
559 
560       case PROP_TEXT_COLUMN:
561 	priv->text_column = g_value_get_int (value);
562         break;
563 
564       case PROP_INLINE_COMPLETION:
565 	priv->inline_completion = g_value_get_boolean (value);
566         break;
567 
568       case PROP_POPUP_COMPLETION:
569 	priv->popup_completion = g_value_get_boolean (value);
570         break;
571 
572       case PROP_POPUP_SET_WIDTH:
573 	priv->popup_set_width = g_value_get_boolean (value);
574         break;
575 
576       case PROP_POPUP_SINGLE_MATCH:
577 	priv->popup_single_match = g_value_get_boolean (value);
578         break;
579 
580       case PROP_INLINE_SELECTION:
581         priv->inline_selection = g_value_get_boolean (value);
582         break;
583 
584       default:
585         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586         break;
587     }
588 }
589 
590 static void
gtk_entry_completion_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)591 gtk_entry_completion_get_property (GObject    *object,
592                                    guint       prop_id,
593                                    GValue     *value,
594                                    GParamSpec *pspec)
595 {
596   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
597 
598   switch (prop_id)
599     {
600       case PROP_MODEL:
601         g_value_set_object (value,
602                             gtk_entry_completion_get_model (completion));
603         break;
604 
605       case PROP_MINIMUM_KEY_LENGTH:
606         g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
607         break;
608 
609       case PROP_TEXT_COLUMN:
610         g_value_set_int (value, gtk_entry_completion_get_text_column (completion));
611         break;
612 
613       case PROP_INLINE_COMPLETION:
614         g_value_set_boolean (value, gtk_entry_completion_get_inline_completion (completion));
615         break;
616 
617       case PROP_POPUP_COMPLETION:
618         g_value_set_boolean (value, gtk_entry_completion_get_popup_completion (completion));
619         break;
620 
621       case PROP_POPUP_SET_WIDTH:
622         g_value_set_boolean (value, gtk_entry_completion_get_popup_set_width (completion));
623         break;
624 
625       case PROP_POPUP_SINGLE_MATCH:
626         g_value_set_boolean (value, gtk_entry_completion_get_popup_single_match (completion));
627         break;
628 
629       case PROP_INLINE_SELECTION:
630         g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion));
631         break;
632 
633       default:
634         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
635         break;
636     }
637 }
638 
639 static void
gtk_entry_completion_finalize(GObject * object)640 gtk_entry_completion_finalize (GObject *object)
641 {
642   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
643   GtkEntryCompletionPrivate *priv = completion->priv;
644 
645   if (priv->tree_view)
646     gtk_widget_destroy (priv->tree_view);
647 
648   if (priv->entry)
649     gtk_entry_set_completion (GTK_ENTRY (priv->entry), NULL);
650 
651   if (priv->actions)
652     g_object_unref (priv->actions);
653   if (priv->action_view)
654     g_object_unref (priv->action_view);
655 
656   g_free (priv->case_normalized_key);
657   g_free (priv->completion_prefix);
658 
659   if (priv->popup_window)
660     gtk_widget_destroy (priv->popup_window);
661 
662   if (priv->match_notify)
663     (* priv->match_notify) (priv->match_data);
664 
665   G_OBJECT_CLASS (gtk_entry_completion_parent_class)->finalize (object);
666 }
667 
668 /* implement cell layout interface */
669 static void
gtk_entry_completion_pack_start(GtkCellLayout * cell_layout,GtkCellRenderer * cell,gboolean expand)670 gtk_entry_completion_pack_start (GtkCellLayout   *cell_layout,
671                                  GtkCellRenderer *cell,
672                                  gboolean         expand)
673 {
674   GtkEntryCompletionPrivate *priv;
675 
676   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
677 
678   gtk_tree_view_column_pack_start (priv->column, cell, expand);
679 }
680 
681 static void
gtk_entry_completion_pack_end(GtkCellLayout * cell_layout,GtkCellRenderer * cell,gboolean expand)682 gtk_entry_completion_pack_end (GtkCellLayout   *cell_layout,
683                                GtkCellRenderer *cell,
684                                gboolean         expand)
685 {
686   GtkEntryCompletionPrivate *priv;
687 
688   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
689 
690   gtk_tree_view_column_pack_end (priv->column, cell, expand);
691 }
692 
693 static void
gtk_entry_completion_clear(GtkCellLayout * cell_layout)694 gtk_entry_completion_clear (GtkCellLayout *cell_layout)
695 {
696   GtkEntryCompletionPrivate *priv;
697 
698   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
699 
700   gtk_tree_view_column_clear (priv->column);
701 }
702 
703 static void
gtk_entry_completion_add_attribute(GtkCellLayout * cell_layout,GtkCellRenderer * cell,const gchar * attribute,gint column)704 gtk_entry_completion_add_attribute (GtkCellLayout   *cell_layout,
705                                     GtkCellRenderer *cell,
706                                     const gchar     *attribute,
707                                     gint             column)
708 {
709   GtkEntryCompletionPrivate *priv;
710 
711   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
712 
713   gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
714 }
715 
716 static void
gtk_entry_completion_set_cell_data_func(GtkCellLayout * cell_layout,GtkCellRenderer * cell,GtkCellLayoutDataFunc func,gpointer func_data,GDestroyNotify destroy)717 gtk_entry_completion_set_cell_data_func (GtkCellLayout          *cell_layout,
718                                          GtkCellRenderer        *cell,
719                                          GtkCellLayoutDataFunc   func,
720                                          gpointer                func_data,
721                                          GDestroyNotify          destroy)
722 {
723   GtkEntryCompletionPrivate *priv;
724 
725   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
726 
727   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
728                                       cell, func, func_data, destroy);
729 }
730 
731 static void
gtk_entry_completion_clear_attributes(GtkCellLayout * cell_layout,GtkCellRenderer * cell)732 gtk_entry_completion_clear_attributes (GtkCellLayout   *cell_layout,
733                                        GtkCellRenderer *cell)
734 {
735   GtkEntryCompletionPrivate *priv;
736 
737   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
738 
739   gtk_tree_view_column_clear_attributes (priv->column, cell);
740 }
741 
742 static void
gtk_entry_completion_reorder(GtkCellLayout * cell_layout,GtkCellRenderer * cell,gint position)743 gtk_entry_completion_reorder (GtkCellLayout   *cell_layout,
744                               GtkCellRenderer *cell,
745                               gint             position)
746 {
747   GtkEntryCompletionPrivate *priv;
748 
749   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
750 
751   gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
752 }
753 
754 static GList *
gtk_entry_completion_get_cells(GtkCellLayout * cell_layout)755 gtk_entry_completion_get_cells (GtkCellLayout *cell_layout)
756 {
757   GtkEntryCompletionPrivate *priv;
758 
759   priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
760 
761   return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
762 }
763 
764 /* all those callbacks */
765 static gboolean
gtk_entry_completion_default_completion_func(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,gpointer user_data)766 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
767                                               const gchar        *key,
768                                               GtkTreeIter        *iter,
769                                               gpointer            user_data)
770 {
771   gchar *item = NULL;
772   gchar *normalized_string;
773   gchar *case_normalized_string;
774 
775   gboolean ret = FALSE;
776 
777   GtkTreeModel *model;
778 
779   model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
780 
781   g_return_val_if_fail (gtk_tree_model_get_column_type (model, completion->priv->text_column) == G_TYPE_STRING,
782 			FALSE);
783 
784   gtk_tree_model_get (model, iter,
785                       completion->priv->text_column, &item,
786                       -1);
787 
788   if (item != NULL)
789     {
790       normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
791 
792       if (normalized_string != NULL)
793         {
794           case_normalized_string = g_utf8_casefold (normalized_string, -1);
795 
796           if (!strncmp (key, case_normalized_string, strlen (key)))
797 	    ret = TRUE;
798 
799           g_free (case_normalized_string);
800         }
801       g_free (normalized_string);
802     }
803   g_free (item);
804 
805   return ret;
806 }
807 
808 static gboolean
gtk_entry_completion_visible_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)809 gtk_entry_completion_visible_func (GtkTreeModel *model,
810                                    GtkTreeIter  *iter,
811                                    gpointer      data)
812 {
813   gboolean ret = FALSE;
814 
815   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
816 
817   if (!completion->priv->case_normalized_key)
818     return ret;
819 
820   if (completion->priv->match_func)
821     ret = (* completion->priv->match_func) (completion,
822                                             completion->priv->case_normalized_key,
823                                             iter,
824                                             completion->priv->match_data);
825   else if (completion->priv->text_column >= 0)
826     ret = gtk_entry_completion_default_completion_func (completion,
827                                                         completion->priv->case_normalized_key,
828                                                         iter,
829                                                         NULL);
830 
831   return ret;
832 }
833 
834 static gboolean
gtk_entry_completion_popup_key_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)835 gtk_entry_completion_popup_key_event (GtkWidget   *widget,
836                                       GdkEventKey *event,
837                                       gpointer     user_data)
838 {
839   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
840 
841   if (!gtk_widget_get_mapped (completion->priv->popup_window))
842     return FALSE;
843 
844   /* propagate event to the entry */
845   gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
846 
847   return TRUE;
848 }
849 
850 static gboolean
gtk_entry_completion_popup_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)851 gtk_entry_completion_popup_button_press (GtkWidget      *widget,
852                                          GdkEventButton *event,
853                                          gpointer        user_data)
854 {
855   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
856 
857   if (!gtk_widget_get_mapped (completion->priv->popup_window))
858     return FALSE;
859 
860   /* if we come here, it's usually time to popdown */
861   _gtk_entry_completion_popdown (completion);
862 
863   return TRUE;
864 }
865 
866 static gboolean
gtk_entry_completion_list_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)867 gtk_entry_completion_list_button_press (GtkWidget      *widget,
868                                         GdkEventButton *event,
869                                         gpointer        user_data)
870 {
871   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
872   GtkTreePath *path = NULL;
873 
874   if (!gtk_widget_get_mapped (completion->priv->popup_window))
875     return FALSE;
876 
877   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
878                                      event->x, event->y,
879                                      &path, NULL, NULL, NULL))
880     {
881       GtkTreeIter iter;
882       gboolean entry_set;
883       GtkTreeModel *model;
884       GtkTreeIter child_iter;
885 
886       gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
887                                &iter, path);
888       gtk_tree_path_free (path);
889       gtk_tree_model_filter_convert_iter_to_child_iter (completion->priv->filter_model,
890                                                         &child_iter,
891                                                         &iter);
892       model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
893 
894       g_signal_handler_block (completion->priv->entry,
895                               completion->priv->changed_id);
896       g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
897                      0, model, &child_iter, &entry_set);
898       g_signal_handler_unblock (completion->priv->entry,
899                                 completion->priv->changed_id);
900 
901       _gtk_entry_completion_popdown (completion);
902 
903       return TRUE;
904     }
905 
906   return FALSE;
907 }
908 
909 static gboolean
gtk_entry_completion_action_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)910 gtk_entry_completion_action_button_press (GtkWidget      *widget,
911                                           GdkEventButton *event,
912                                           gpointer        user_data)
913 {
914   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
915   GtkTreePath *path = NULL;
916 
917   if (!gtk_widget_get_mapped (completion->priv->popup_window))
918     return FALSE;
919 
920   _gtk_entry_reset_im_context (GTK_ENTRY (completion->priv->entry));
921 
922   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
923                                      event->x, event->y,
924                                      &path, NULL, NULL, NULL))
925     {
926       g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
927                      0, gtk_tree_path_get_indices (path)[0]);
928       gtk_tree_path_free (path);
929 
930       _gtk_entry_completion_popdown (completion);
931       return TRUE;
932     }
933 
934   return FALSE;
935 }
936 
937 static void
gtk_entry_completion_action_data_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)938 gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
939                                        GtkCellRenderer   *cell,
940                                        GtkTreeModel      *model,
941                                        GtkTreeIter       *iter,
942                                        gpointer           data)
943 {
944   gchar *string = NULL;
945   gboolean markup;
946 
947   gtk_tree_model_get (model, iter,
948                       0, &string,
949                       1, &markup,
950                       -1);
951 
952   if (!string)
953     return;
954 
955   if (markup)
956     g_object_set (cell,
957                   "text", NULL,
958                   "markup", string,
959                   NULL);
960   else
961     g_object_set (cell,
962                   "markup", NULL,
963                   "text", string,
964                   NULL);
965 
966   g_free (string);
967 }
968 
969 static void
gtk_entry_completion_selection_changed(GtkTreeSelection * selection,gpointer data)970 gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
971                                         gpointer          data)
972 {
973   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
974 
975   if (completion->priv->first_sel_changed)
976     {
977       completion->priv->first_sel_changed = FALSE;
978       if (gtk_widget_is_focus (completion->priv->tree_view))
979         gtk_tree_selection_unselect_all (selection);
980     }
981 }
982 
983 /* public API */
984 
985 /**
986  * gtk_entry_completion_new:
987  *
988  * Creates a new #GtkEntryCompletion object.
989  *
990  * Return value: A newly created #GtkEntryCompletion object.
991  *
992  * Since: 2.4
993  */
994 GtkEntryCompletion *
gtk_entry_completion_new(void)995 gtk_entry_completion_new (void)
996 {
997   GtkEntryCompletion *completion;
998 
999   completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
1000 
1001   return completion;
1002 }
1003 
1004 /**
1005  * gtk_entry_completion_get_entry:
1006  * @completion: A #GtkEntryCompletion.
1007  *
1008  * Gets the entry @completion has been attached to.
1009  *
1010  * Return value: (transfer none): The entry @completion has been attached to.
1011  *
1012  * Since: 2.4
1013  */
1014 GtkWidget *
gtk_entry_completion_get_entry(GtkEntryCompletion * completion)1015 gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
1016 {
1017   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1018 
1019   return completion->priv->entry;
1020 }
1021 
1022 /**
1023  * gtk_entry_completion_set_model:
1024  * @completion: A #GtkEntryCompletion.
1025  * @model: (allow-none): The #GtkTreeModel.
1026  *
1027  * Sets the model for a #GtkEntryCompletion. If @completion already has
1028  * a model set, it will remove it before setting the new model.
1029  * If model is %NULL, then it will unset the model.
1030  *
1031  * Since: 2.4
1032  */
1033 void
gtk_entry_completion_set_model(GtkEntryCompletion * completion,GtkTreeModel * model)1034 gtk_entry_completion_set_model (GtkEntryCompletion *completion,
1035                                 GtkTreeModel       *model)
1036 {
1037   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1038   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1039 
1040   if (!model)
1041     {
1042       gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
1043 			       NULL);
1044       _gtk_entry_completion_popdown (completion);
1045       completion->priv->filter_model = NULL;
1046       return;
1047     }
1048 
1049   /* code will unref the old filter model (if any) */
1050   completion->priv->filter_model =
1051     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
1052   gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
1053                                           gtk_entry_completion_visible_func,
1054                                           completion,
1055                                           NULL);
1056 
1057   gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
1058                            GTK_TREE_MODEL (completion->priv->filter_model));
1059   g_object_unref (completion->priv->filter_model);
1060 
1061   g_object_notify (G_OBJECT (completion), "model");
1062 
1063   if (gtk_widget_get_visible (completion->priv->popup_window))
1064     _gtk_entry_completion_resize_popup (completion);
1065 }
1066 
1067 /**
1068  * gtk_entry_completion_get_model:
1069  * @completion: A #GtkEntryCompletion.
1070  *
1071  * Returns the model the #GtkEntryCompletion is using as data source.
1072  * Returns %NULL if the model is unset.
1073  *
1074  * Return value: (transfer none): A #GtkTreeModel, or %NULL if none
1075  *     is currently being used.
1076  *
1077  * Since: 2.4
1078  */
1079 GtkTreeModel *
gtk_entry_completion_get_model(GtkEntryCompletion * completion)1080 gtk_entry_completion_get_model (GtkEntryCompletion *completion)
1081 {
1082   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1083 
1084   if (!completion->priv->filter_model)
1085     return NULL;
1086 
1087   return gtk_tree_model_filter_get_model (completion->priv->filter_model);
1088 }
1089 
1090 /**
1091  * gtk_entry_completion_set_match_func:
1092  * @completion: A #GtkEntryCompletion.
1093  * @func: The #GtkEntryCompletionMatchFunc to use.
1094  * @func_data: The user data for @func.
1095  * @func_notify: Destroy notifier for @func_data.
1096  *
1097  * Sets the match function for @completion to be @func. The match function
1098  * is used to determine if a row should or should not be in the completion
1099  * list.
1100  *
1101  * Since: 2.4
1102  */
1103 void
gtk_entry_completion_set_match_func(GtkEntryCompletion * completion,GtkEntryCompletionMatchFunc func,gpointer func_data,GDestroyNotify func_notify)1104 gtk_entry_completion_set_match_func (GtkEntryCompletion          *completion,
1105                                      GtkEntryCompletionMatchFunc  func,
1106                                      gpointer                     func_data,
1107                                      GDestroyNotify               func_notify)
1108 {
1109   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1110 
1111   if (completion->priv->match_notify)
1112     (* completion->priv->match_notify) (completion->priv->match_data);
1113 
1114   completion->priv->match_func = func;
1115   completion->priv->match_data = func_data;
1116   completion->priv->match_notify = func_notify;
1117 }
1118 
1119 /**
1120  * gtk_entry_completion_set_minimum_key_length:
1121  * @completion: A #GtkEntryCompletion.
1122  * @length: The minimum length of the key in order to start completing.
1123  *
1124  * Requires the length of the search key for @completion to be at least
1125  * @length. This is useful for long lists, where completing using a small
1126  * key takes a lot of time and will come up with meaningless results anyway
1127  * (ie, a too large dataset).
1128  *
1129  * Since: 2.4
1130  */
1131 void
gtk_entry_completion_set_minimum_key_length(GtkEntryCompletion * completion,gint length)1132 gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
1133                                              gint                length)
1134 {
1135   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1136   g_return_if_fail (length >= 0);
1137 
1138   if (completion->priv->minimum_key_length != length)
1139     {
1140       completion->priv->minimum_key_length = length;
1141 
1142       g_object_notify (G_OBJECT (completion), "minimum-key-length");
1143     }
1144 }
1145 
1146 /**
1147  * gtk_entry_completion_get_minimum_key_length:
1148  * @completion: A #GtkEntryCompletion.
1149  *
1150  * Returns the minimum key length as set for @completion.
1151  *
1152  * Return value: The currently used minimum key length.
1153  *
1154  * Since: 2.4
1155  */
1156 gint
gtk_entry_completion_get_minimum_key_length(GtkEntryCompletion * completion)1157 gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
1158 {
1159   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
1160 
1161   return completion->priv->minimum_key_length;
1162 }
1163 
1164 /**
1165  * gtk_entry_completion_complete:
1166  * @completion: A #GtkEntryCompletion.
1167  *
1168  * Requests a completion operation, or in other words a refiltering of the
1169  * current list with completions, using the current key. The completion list
1170  * view will be updated accordingly.
1171  *
1172  * Since: 2.4
1173  */
1174 void
gtk_entry_completion_complete(GtkEntryCompletion * completion)1175 gtk_entry_completion_complete (GtkEntryCompletion *completion)
1176 {
1177   gchar *tmp;
1178 
1179   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1180 
1181   if (!completion->priv->filter_model)
1182     return;
1183 
1184   g_free (completion->priv->case_normalized_key);
1185 
1186   tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
1187                           -1, G_NORMALIZE_ALL);
1188   completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
1189   g_free (tmp);
1190 
1191   gtk_tree_model_filter_refilter (completion->priv->filter_model);
1192 
1193   if (gtk_widget_get_visible (completion->priv->popup_window))
1194     _gtk_entry_completion_resize_popup (completion);
1195 }
1196 
1197 static void
gtk_entry_completion_insert_action(GtkEntryCompletion * completion,gint index,const gchar * string,gboolean markup)1198 gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
1199                                     gint                index,
1200                                     const gchar        *string,
1201                                     gboolean            markup)
1202 {
1203   GtkTreeIter iter;
1204 
1205   gtk_list_store_insert (completion->priv->actions, &iter, index);
1206   gtk_list_store_set (completion->priv->actions, &iter,
1207                       0, string,
1208                       1, markup,
1209                       -1);
1210 
1211   if (!completion->priv->action_view->parent)
1212     {
1213       GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
1214 
1215       gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
1216                                 path, NULL, FALSE);
1217       gtk_tree_path_free (path);
1218 
1219       gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
1220                           completion->priv->action_view, FALSE, FALSE, 0);
1221       gtk_widget_show (completion->priv->action_view);
1222     }
1223 }
1224 
1225 /**
1226  * gtk_entry_completion_insert_action_text:
1227  * @completion: A #GtkEntryCompletion.
1228  * @index_: The index of the item to insert.
1229  * @text: Text of the item to insert.
1230  *
1231  * Inserts an action in @completion's action item list at position @index_
1232  * with text @text. If you want the action item to have markup, use
1233  * gtk_entry_completion_insert_action_markup().
1234  *
1235  * Since: 2.4
1236  */
1237 void
gtk_entry_completion_insert_action_text(GtkEntryCompletion * completion,gint index_,const gchar * text)1238 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
1239                                          gint                index_,
1240                                          const gchar        *text)
1241 {
1242   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1243   g_return_if_fail (text != NULL);
1244 
1245   gtk_entry_completion_insert_action (completion, index_, text, FALSE);
1246 }
1247 
1248 /**
1249  * gtk_entry_completion_insert_action_markup:
1250  * @completion: A #GtkEntryCompletion.
1251  * @index_: The index of the item to insert.
1252  * @markup: Markup of the item to insert.
1253  *
1254  * Inserts an action in @completion's action item list at position @index_
1255  * with markup @markup.
1256  *
1257  * Since: 2.4
1258  */
1259 void
gtk_entry_completion_insert_action_markup(GtkEntryCompletion * completion,gint index_,const gchar * markup)1260 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
1261                                            gint                index_,
1262                                            const gchar        *markup)
1263 {
1264   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1265   g_return_if_fail (markup != NULL);
1266 
1267   gtk_entry_completion_insert_action (completion, index_, markup, TRUE);
1268 }
1269 
1270 /**
1271  * gtk_entry_completion_delete_action:
1272  * @completion: A #GtkEntryCompletion.
1273  * @index_: The index of the item to Delete.
1274  *
1275  * Deletes the action at @index_ from @completion's action list.
1276  *
1277  * Since: 2.4
1278  */
1279 void
gtk_entry_completion_delete_action(GtkEntryCompletion * completion,gint index_)1280 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
1281                                     gint                index_)
1282 {
1283   GtkTreeIter iter;
1284 
1285   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1286   g_return_if_fail (index_ >= 0);
1287 
1288   gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
1289                                  &iter, NULL, index_);
1290   gtk_list_store_remove (completion->priv->actions, &iter);
1291 }
1292 
1293 /**
1294  * gtk_entry_completion_set_text_column:
1295  * @completion: A #GtkEntryCompletion.
1296  * @column: The column in the model of @completion to get strings from.
1297  *
1298  * Convenience function for setting up the most used case of this code: a
1299  * completion list with just strings. This function will set up @completion
1300  * to have a list displaying all (and just) strings in the completion list,
1301  * and to get those strings from @column in the model of @completion.
1302  *
1303  * This functions creates and adds a #GtkCellRendererText for the selected
1304  * column. If you need to set the text column, but don't want the cell
1305  * renderer, use g_object_set() to set the ::text_column property directly.
1306  *
1307  * Since: 2.4
1308  */
1309 void
gtk_entry_completion_set_text_column(GtkEntryCompletion * completion,gint column)1310 gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
1311                                       gint                column)
1312 {
1313   GtkCellRenderer *cell;
1314 
1315   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1316   g_return_if_fail (column >= 0);
1317 
1318   completion->priv->text_column = column;
1319 
1320   cell = gtk_cell_renderer_text_new ();
1321   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1322                               cell, TRUE);
1323   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1324                                  cell,
1325                                  "text", column);
1326 
1327   g_object_notify (G_OBJECT (completion), "text-column");
1328 }
1329 
1330 /**
1331  * gtk_entry_completion_get_text_column:
1332  * @completion: a #GtkEntryCompletion
1333  *
1334  * Returns the column in the model of @completion to get strings from.
1335  *
1336  * Return value: the column containing the strings
1337  *
1338  * Since: 2.6
1339  **/
1340 gint
gtk_entry_completion_get_text_column(GtkEntryCompletion * completion)1341 gtk_entry_completion_get_text_column (GtkEntryCompletion *completion)
1342 {
1343   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), -1);
1344 
1345   return completion->priv->text_column;
1346 }
1347 
1348 /* private */
1349 
1350 static gboolean
gtk_entry_completion_list_enter_notify(GtkWidget * widget,GdkEventCrossing * event,gpointer data)1351 gtk_entry_completion_list_enter_notify (GtkWidget        *widget,
1352 					GdkEventCrossing *event,
1353 					gpointer          data)
1354 {
1355   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1356 
1357   return completion->priv->ignore_enter;
1358 }
1359 
1360 static gboolean
gtk_entry_completion_list_motion_notify(GtkWidget * widget,GdkEventMotion * event,gpointer data)1361 gtk_entry_completion_list_motion_notify (GtkWidget      *widget,
1362 					 GdkEventMotion *event,
1363 					 gpointer        data)
1364 {
1365   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
1366 
1367   completion->priv->ignore_enter = FALSE;
1368 
1369   return FALSE;
1370 }
1371 
1372 
1373 /* some nasty size requisition */
1374 gboolean
_gtk_entry_completion_resize_popup(GtkEntryCompletion * completion)1375 _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
1376 {
1377   gint x, y;
1378   gint matches, actions, items, height, x_border, y_border;
1379   GdkScreen *screen;
1380   gint monitor_num;
1381   gint vertical_separator;
1382   GdkRectangle monitor;
1383   GtkRequisition popup_req;
1384   GtkRequisition entry_req;
1385   GtkTreePath *path;
1386   gboolean above;
1387   gint width;
1388   GtkTreeViewColumn *action_column;
1389   gint action_height;
1390 
1391   if (!completion->priv->entry->window)
1392     return FALSE;
1393 
1394   gdk_window_get_origin (completion->priv->entry->window, &x, &y);
1395   _gtk_entry_get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
1396 
1397   matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
1398   actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
1399   action_column  = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1400 
1401   gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
1402                                       NULL, NULL, NULL, &height);
1403   gtk_tree_view_column_cell_get_size (action_column, NULL,
1404                                       NULL, NULL, NULL, &action_height);
1405 
1406   gtk_widget_style_get (GTK_WIDGET (completion->priv->tree_view),
1407                         "vertical-separator", &vertical_separator,
1408                         NULL);
1409 
1410   height += vertical_separator;
1411 
1412   gtk_widget_realize (completion->priv->tree_view);
1413 
1414   screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
1415   monitor_num = gdk_screen_get_monitor_at_window (screen,
1416 						  GTK_WIDGET (completion->priv->entry)->window);
1417   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1418 
1419 
1420 
1421   if (y > monitor.height / 2)
1422     items = MIN (matches, (((monitor.y + y) - (actions * action_height)) / height) - 1);
1423   else
1424     items = MIN (matches, (((monitor.height - y) - (actions * action_height)) / height) - 1);
1425 
1426   if (items <= 0)
1427     gtk_widget_hide (completion->priv->scrolled_window);
1428   else
1429     gtk_widget_show (completion->priv->scrolled_window);
1430 
1431   if (completion->priv->popup_set_width)
1432     width = MIN (completion->priv->entry->allocation.width, monitor.width) - 2 * x_border;
1433   else
1434     width = -1;
1435 
1436   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (completion->priv->tree_view));
1437   gtk_widget_set_size_request (completion->priv->tree_view, width, items * height);
1438 
1439   if (actions)
1440     {
1441       gtk_widget_show (completion->priv->action_view);
1442       gtk_widget_set_size_request (completion->priv->action_view, width, -1);
1443     }
1444   else
1445     gtk_widget_hide (completion->priv->action_view);
1446 
1447   gtk_widget_size_request (completion->priv->popup_window, &popup_req);
1448   gtk_widget_size_request (completion->priv->entry, &entry_req);
1449 
1450   if (x < monitor.x)
1451     x = monitor.x;
1452   else if (x + popup_req.width > monitor.x + monitor.width)
1453     x = monitor.x + monitor.width - popup_req.width;
1454 
1455   if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height ||
1456       y - monitor.y < (monitor.y + monitor.height) - (y + entry_req.height))
1457     {
1458       y += entry_req.height;
1459       above = FALSE;
1460     }
1461   else
1462     {
1463       y -= popup_req.height;
1464       above = TRUE;
1465     }
1466 
1467   if (matches > 0)
1468     {
1469       path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
1470       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path,
1471 				    NULL, FALSE, 0.0, 0.0);
1472       gtk_tree_path_free (path);
1473     }
1474 
1475   gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
1476 
1477   return above;
1478 }
1479 
1480 void
_gtk_entry_completion_popup(GtkEntryCompletion * completion)1481 _gtk_entry_completion_popup (GtkEntryCompletion *completion)
1482 {
1483   GtkTreeViewColumn *column;
1484   GList *renderers;
1485   GtkWidget *toplevel;
1486 
1487   if (gtk_widget_get_mapped (completion->priv->popup_window))
1488     return;
1489 
1490   if (!gtk_widget_get_mapped (completion->priv->entry))
1491     return;
1492 
1493   if (!gtk_widget_has_focus (completion->priv->entry))
1494     return;
1495 
1496   completion->priv->ignore_enter = TRUE;
1497 
1498   column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1499   renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1500   gtk_widget_ensure_style (completion->priv->tree_view);
1501   g_object_set (GTK_CELL_RENDERER (renderers->data), "cell-background-gdk",
1502                 &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
1503                 NULL);
1504   g_list_free (renderers);
1505 
1506   gtk_widget_show_all (completion->priv->vbox);
1507 
1508   /* default on no match */
1509   completion->priv->current_selected = -1;
1510 
1511   _gtk_entry_completion_resize_popup (completion);
1512 
1513   toplevel = gtk_widget_get_toplevel (completion->priv->entry);
1514   if (GTK_IS_WINDOW (toplevel))
1515     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1516 				 GTK_WINDOW (completion->priv->popup_window));
1517 
1518   /* prevent the first row being focused */
1519   gtk_widget_grab_focus (completion->priv->tree_view);
1520 
1521   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
1522   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
1523 
1524   gtk_window_set_screen (GTK_WINDOW (completion->priv->popup_window),
1525                          gtk_widget_get_screen (completion->priv->entry));
1526 
1527   gtk_widget_show (completion->priv->popup_window);
1528 
1529   gtk_grab_add (completion->priv->popup_window);
1530   gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
1531                     GDK_BUTTON_PRESS_MASK |
1532                     GDK_BUTTON_RELEASE_MASK |
1533                     GDK_POINTER_MOTION_MASK,
1534                     NULL, NULL, GDK_CURRENT_TIME);
1535 }
1536 
1537 void
_gtk_entry_completion_popdown(GtkEntryCompletion * completion)1538 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
1539 {
1540   if (!gtk_widget_get_mapped (completion->priv->popup_window))
1541     return;
1542 
1543   completion->priv->ignore_enter = FALSE;
1544 
1545   gdk_pointer_ungrab (GDK_CURRENT_TIME);
1546   gtk_grab_remove (completion->priv->popup_window);
1547 
1548   gtk_widget_hide (completion->priv->popup_window);
1549 }
1550 
1551 static gboolean
gtk_entry_completion_match_selected(GtkEntryCompletion * completion,GtkTreeModel * model,GtkTreeIter * iter)1552 gtk_entry_completion_match_selected (GtkEntryCompletion *completion,
1553 				     GtkTreeModel       *model,
1554 				     GtkTreeIter        *iter)
1555 {
1556   gchar *str = NULL;
1557 
1558   gtk_tree_model_get (model, iter, completion->priv->text_column, &str, -1);
1559   gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str ? str : "");
1560 
1561   /* move cursor to the end */
1562   gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry), -1);
1563 
1564   g_free (str);
1565 
1566   return TRUE;
1567 }
1568 
1569 static gboolean
gtk_entry_completion_cursor_on_match(GtkEntryCompletion * completion,GtkTreeModel * model,GtkTreeIter * iter)1570 gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion,
1571 				      GtkTreeModel       *model,
1572 				      GtkTreeIter        *iter)
1573 {
1574   gtk_entry_completion_insert_completion (completion, model, iter);
1575 
1576   return TRUE;
1577 }
1578 
1579 gchar *
_gtk_entry_completion_compute_prefix(GtkEntryCompletion * completion,const char * key)1580 _gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion,
1581 				      const char         *key)
1582 {
1583   GtkTreeIter iter;
1584   gchar *prefix = NULL;
1585   gboolean valid;
1586 
1587   if (completion->priv->text_column < 0)
1588     return NULL;
1589 
1590   valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (completion->priv->filter_model),
1591 					 &iter);
1592 
1593   while (valid)
1594     {
1595       gchar *text;
1596 
1597       gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
1598 			  &iter, completion->priv->text_column, &text,
1599 			  -1);
1600 
1601       if (text && g_str_has_prefix (text, key))
1602 	{
1603 	  if (!prefix)
1604 	    prefix = g_strdup (text);
1605 	  else
1606 	    {
1607 	      gchar *p = prefix;
1608 	      gchar *q = text;
1609 
1610 	      while (*p && *p == *q)
1611 		{
1612 		  p++;
1613 		  q++;
1614 		}
1615 
1616 	      *p = '\0';
1617 
1618               if (p > prefix)
1619                 {
1620                   /* strip a partial multibyte character */
1621                   q = g_utf8_find_prev_char (prefix, p);
1622                   switch (g_utf8_get_char_validated (q, p - q))
1623                     {
1624                     case (gunichar)-2:
1625                     case (gunichar)-1:
1626                       *q = 0;
1627                     default: ;
1628                     }
1629                 }
1630 	    }
1631 	}
1632 
1633       g_free (text);
1634       valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (completion->priv->filter_model),
1635 					&iter);
1636     }
1637 
1638   return prefix;
1639 }
1640 
1641 
1642 static gboolean
gtk_entry_completion_real_insert_prefix(GtkEntryCompletion * completion,const gchar * prefix)1643 gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
1644 					 const gchar        *prefix)
1645 {
1646   if (prefix)
1647     {
1648       gint key_len;
1649       gint prefix_len;
1650       const gchar *key;
1651 
1652       prefix_len = g_utf8_strlen (prefix, -1);
1653 
1654       key = gtk_entry_get_text (GTK_ENTRY (completion->priv->entry));
1655       key_len = g_utf8_strlen (key, -1);
1656 
1657       if (prefix_len > key_len)
1658 	{
1659 	  gint pos = prefix_len;
1660 
1661 	  gtk_editable_insert_text (GTK_EDITABLE (completion->priv->entry),
1662 				    prefix + strlen (key), -1, &pos);
1663 	  gtk_editable_select_region (GTK_EDITABLE (completion->priv->entry),
1664 				      key_len, prefix_len);
1665 
1666 	  completion->priv->has_completion = TRUE;
1667 	}
1668     }
1669 
1670   return TRUE;
1671 }
1672 
1673 /**
1674  * gtk_entry_completion_get_completion_prefix:
1675  * @completion: a #GtkEntryCompletion
1676  *
1677  * Get the original text entered by the user that triggered
1678  * the completion or %NULL if there's no completion ongoing.
1679  *
1680  * Returns: the prefix for the current completion
1681  *
1682  * Since: 2.12
1683  **/
1684 const gchar*
gtk_entry_completion_get_completion_prefix(GtkEntryCompletion * completion)1685 gtk_entry_completion_get_completion_prefix (GtkEntryCompletion *completion)
1686 {
1687   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
1688 
1689   return completion->priv->completion_prefix;
1690 }
1691 
1692 static void
gtk_entry_completion_insert_completion_text(GtkEntryCompletion * completion,const gchar * text)1693 gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
1694 					     const gchar *text)
1695 {
1696   GtkEntryCompletionPrivate *priv = completion->priv;
1697   gint len;
1698 
1699   priv = completion->priv;
1700 
1701   if (priv->changed_id > 0)
1702     g_signal_handler_block (priv->entry, priv->changed_id);
1703 
1704   if (priv->insert_text_id > 0)
1705     g_signal_handler_block (priv->entry, priv->insert_text_id);
1706 
1707   gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
1708 
1709   len = strlen (priv->completion_prefix);
1710   gtk_editable_select_region (GTK_EDITABLE (priv->entry), len, -1);
1711 
1712   if (priv->changed_id > 0)
1713     g_signal_handler_unblock (priv->entry, priv->changed_id);
1714 
1715   if (priv->insert_text_id > 0)
1716     g_signal_handler_unblock (priv->entry, priv->insert_text_id);
1717 }
1718 
1719 static gboolean
gtk_entry_completion_insert_completion(GtkEntryCompletion * completion,GtkTreeModel * model,GtkTreeIter * iter)1720 gtk_entry_completion_insert_completion (GtkEntryCompletion *completion,
1721 					GtkTreeModel       *model,
1722 					GtkTreeIter        *iter)
1723 {
1724   gchar *str = NULL;
1725 
1726   if (completion->priv->text_column < 0)
1727     return FALSE;
1728 
1729   gtk_tree_model_get (model, iter,
1730 		      completion->priv->text_column, &str,
1731 		      -1);
1732 
1733   gtk_entry_completion_insert_completion_text (completion, str);
1734 
1735   g_free (str);
1736 
1737   return TRUE;
1738 }
1739 
1740 /**
1741  * gtk_entry_completion_insert_prefix:
1742  * @completion: a #GtkEntryCompletion
1743  *
1744  * Requests a prefix insertion.
1745  *
1746  * Since: 2.6
1747  **/
1748 
1749 void
gtk_entry_completion_insert_prefix(GtkEntryCompletion * completion)1750 gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
1751 {
1752   gboolean done;
1753   gchar *prefix;
1754 
1755   if (completion->priv->insert_text_id > 0)
1756     g_signal_handler_block (completion->priv->entry,
1757                             completion->priv->insert_text_id);
1758 
1759   prefix = _gtk_entry_completion_compute_prefix (completion,
1760 						 gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)));
1761   if (prefix)
1762     {
1763       g_signal_emit (completion, entry_completion_signals[INSERT_PREFIX],
1764 		     0, prefix, &done);
1765       g_free (prefix);
1766     }
1767 
1768   if (completion->priv->insert_text_id > 0)
1769     g_signal_handler_unblock (completion->priv->entry,
1770                               completion->priv->insert_text_id);
1771 }
1772 
1773 /**
1774  * gtk_entry_completion_set_inline_completion:
1775  * @completion: a #GtkEntryCompletion
1776  * @inline_completion: %TRUE to do inline completion
1777  *
1778  * Sets whether the common prefix of the possible completions should
1779  * be automatically inserted in the entry.
1780  *
1781  * Since: 2.6
1782  **/
1783 void
gtk_entry_completion_set_inline_completion(GtkEntryCompletion * completion,gboolean inline_completion)1784 gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
1785 					    gboolean            inline_completion)
1786 {
1787   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1788 
1789   inline_completion = inline_completion != FALSE;
1790 
1791   if (completion->priv->inline_completion != inline_completion)
1792     {
1793       completion->priv->inline_completion = inline_completion;
1794 
1795       g_object_notify (G_OBJECT (completion), "inline-completion");
1796     }
1797 }
1798 
1799 /**
1800  * gtk_entry_completion_get_inline_completion:
1801  * @completion: a #GtkEntryCompletion
1802  *
1803  * Returns whether the common prefix of the possible completions should
1804  * be automatically inserted in the entry.
1805  *
1806  * Return value: %TRUE if inline completion is turned on
1807  *
1808  * Since: 2.6
1809  **/
1810 gboolean
gtk_entry_completion_get_inline_completion(GtkEntryCompletion * completion)1811 gtk_entry_completion_get_inline_completion (GtkEntryCompletion *completion)
1812 {
1813   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1814 
1815   return completion->priv->inline_completion;
1816 }
1817 
1818 /**
1819  * gtk_entry_completion_set_popup_completion:
1820  * @completion: a #GtkEntryCompletion
1821  * @popup_completion: %TRUE to do popup completion
1822  *
1823  * Sets whether the completions should be presented in a popup window.
1824  *
1825  * Since: 2.6
1826  **/
1827 void
gtk_entry_completion_set_popup_completion(GtkEntryCompletion * completion,gboolean popup_completion)1828 gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion,
1829 					   gboolean            popup_completion)
1830 {
1831   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1832 
1833   popup_completion = popup_completion != FALSE;
1834 
1835   if (completion->priv->popup_completion != popup_completion)
1836     {
1837       completion->priv->popup_completion = popup_completion;
1838 
1839       g_object_notify (G_OBJECT (completion), "popup-completion");
1840     }
1841 }
1842 
1843 
1844 /**
1845  * gtk_entry_completion_get_popup_completion:
1846  * @completion: a #GtkEntryCompletion
1847  *
1848  * Returns whether the completions should be presented in a popup window.
1849  *
1850  * Return value: %TRUE if popup completion is turned on
1851  *
1852  * Since: 2.6
1853  **/
1854 gboolean
gtk_entry_completion_get_popup_completion(GtkEntryCompletion * completion)1855 gtk_entry_completion_get_popup_completion (GtkEntryCompletion *completion)
1856 {
1857   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1858 
1859   return completion->priv->popup_completion;
1860 }
1861 
1862 /**
1863  * gtk_entry_completion_set_popup_set_width:
1864  * @completion: a #GtkEntryCompletion
1865  * @popup_set_width: %TRUE to make the width of the popup the same as the entry
1866  *
1867  * Sets whether the completion popup window will be resized to be the same
1868  * width as the entry.
1869  *
1870  * Since: 2.8
1871  */
1872 void
gtk_entry_completion_set_popup_set_width(GtkEntryCompletion * completion,gboolean popup_set_width)1873 gtk_entry_completion_set_popup_set_width (GtkEntryCompletion *completion,
1874 					  gboolean            popup_set_width)
1875 {
1876   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1877 
1878   popup_set_width = popup_set_width != FALSE;
1879 
1880   if (completion->priv->popup_set_width != popup_set_width)
1881     {
1882       completion->priv->popup_set_width = popup_set_width;
1883 
1884       g_object_notify (G_OBJECT (completion), "popup-set-width");
1885     }
1886 }
1887 
1888 /**
1889  * gtk_entry_completion_get_popup_set_width:
1890  * @completion: a #GtkEntryCompletion
1891  *
1892  * Returns whether the  completion popup window will be resized to the
1893  * width of the entry.
1894  *
1895  * Return value: %TRUE if the popup window will be resized to the width of
1896  *   the entry
1897  *
1898  * Since: 2.8
1899  **/
1900 gboolean
gtk_entry_completion_get_popup_set_width(GtkEntryCompletion * completion)1901 gtk_entry_completion_get_popup_set_width (GtkEntryCompletion *completion)
1902 {
1903   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1904 
1905   return completion->priv->popup_set_width;
1906 }
1907 
1908 
1909 /**
1910  * gtk_entry_completion_set_popup_single_match:
1911  * @completion: a #GtkEntryCompletion
1912  * @popup_single_match: %TRUE if the popup should appear even for a single
1913  *   match
1914  *
1915  * Sets whether the completion popup window will appear even if there is
1916  * only a single match. You may want to set this to %FALSE if you
1917  * are using <link linkend="GtkEntryCompletion--inline-completion">inline
1918  * completion</link>.
1919  *
1920  * Since: 2.8
1921  */
1922 void
gtk_entry_completion_set_popup_single_match(GtkEntryCompletion * completion,gboolean popup_single_match)1923 gtk_entry_completion_set_popup_single_match (GtkEntryCompletion *completion,
1924 					     gboolean            popup_single_match)
1925 {
1926   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1927 
1928   popup_single_match = popup_single_match != FALSE;
1929 
1930   if (completion->priv->popup_single_match != popup_single_match)
1931     {
1932       completion->priv->popup_single_match = popup_single_match;
1933 
1934       g_object_notify (G_OBJECT (completion), "popup-single-match");
1935     }
1936 }
1937 
1938 /**
1939  * gtk_entry_completion_get_popup_single_match:
1940  * @completion: a #GtkEntryCompletion
1941  *
1942  * Returns whether the completion popup window will appear even if there is
1943  * only a single match.
1944  *
1945  * Return value: %TRUE if the popup window will appear regardless of the
1946  *    number of matches.
1947  *
1948  * Since: 2.8
1949  **/
1950 gboolean
gtk_entry_completion_get_popup_single_match(GtkEntryCompletion * completion)1951 gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion)
1952 {
1953   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), TRUE);
1954 
1955   return completion->priv->popup_single_match;
1956 }
1957 
1958 /**
1959  * gtk_entry_completion_set_inline_selection:
1960  * @completion: a #GtkEntryCompletion
1961  * @inline_selection: %TRUE to do inline selection
1962  *
1963  * Sets whether it is possible to cycle through the possible completions
1964  * inside the entry.
1965  *
1966  * Since: 2.12
1967  **/
1968 void
gtk_entry_completion_set_inline_selection(GtkEntryCompletion * completion,gboolean inline_selection)1969 gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion,
1970 					   gboolean inline_selection)
1971 {
1972   g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1973 
1974   inline_selection = inline_selection != FALSE;
1975 
1976   if (completion->priv->inline_selection != inline_selection)
1977     {
1978       completion->priv->inline_selection = inline_selection;
1979 
1980       g_object_notify (G_OBJECT (completion), "inline-selection");
1981     }
1982 }
1983 
1984 /**
1985  * gtk_entry_completion_get_inline_selection:
1986  * @completion: a #GtkEntryCompletion
1987  *
1988  * Returns %TRUE if inline-selection mode is turned on.
1989  *
1990  * Returns: %TRUE if inline-selection mode is on
1991  *
1992  * Since: 2.12
1993  **/
1994 gboolean
gtk_entry_completion_get_inline_selection(GtkEntryCompletion * completion)1995 gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion)
1996 {
1997   g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1998 
1999   return completion->priv->inline_selection;
2000 }
2001 
2002 #define __GTK_ENTRY_COMPLETION_C__
2003 #include "gtkaliasdef.c"
2004