1 /* gtkentrycompletion.c
2 * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
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.
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.
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.
21 #include "gtkentrycompletion.h"
22 #include "gtkentryprivate.h"
23 #include "gtkcelllayout.h"
26 #include "gtkcellrenderertext.h"
28 #include "gtktreeselection.h"
29 #include "gtktreeview.h"
30 #include "gtkscrolledwindow.h"
32 #include "gtkwindow.h"
35 #include "gtksignal.h"
36 #include "gtkmarshalers.h"
54 PROP_MINIMUM_KEY_LENGTH
57 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
59 static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass);
60 static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface);
61 static void gtk_entry_completion_init (GtkEntryCompletion *completion);
62 static void gtk_entry_completion_set_property (GObject *object,
66 static void gtk_entry_completion_get_property (GObject *object,
70 static void gtk_entry_completion_finalize (GObject *object);
72 static void gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
73 GtkCellRenderer *cell,
75 static void gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
76 GtkCellRenderer *cell,
78 static void gtk_entry_completion_clear (GtkCellLayout *cell_layout);
79 static void gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
80 GtkCellRenderer *cell,
81 const char *attribute,
83 static void gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
84 GtkCellRenderer *cell,
85 GtkCellLayoutDataFunc func,
87 GDestroyNotify destroy);
88 static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
89 GtkCellRenderer *cell);
90 static void gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
91 GtkCellRenderer *cell,
94 static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model,
97 static gboolean gtk_entry_completion_popup_key_press (GtkWidget *widget,
100 static gboolean gtk_entry_completion_popup_button_press (GtkWidget *widget,
101 GdkEventButton *event,
103 static gboolean gtk_entry_completion_list_button_press (GtkWidget *widget,
104 GdkEventButton *event,
106 static gboolean gtk_entry_completion_action_button_press (GtkWidget *widget,
107 GdkEventButton *event,
109 static void gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
112 static void gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
116 static void gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
117 GtkCellRenderer *cell,
123 static GObjectClass *parent_class = NULL;
124 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
128 gtk_entry_completion_get_type (void)
130 static GType entry_completion_type = 0;
132 if (!entry_completion_type)
134 static const GTypeInfo entry_completion_info =
136 sizeof (GtkEntryCompletionClass),
139 (GClassInitFunc) gtk_entry_completion_class_init,
142 sizeof (GtkEntryCompletion),
144 (GInstanceInitFunc) gtk_entry_completion_init
147 static const GInterfaceInfo cell_layout_info =
149 (GInterfaceInitFunc) gtk_entry_completion_cell_layout_init,
154 entry_completion_type =
155 g_type_register_static (G_TYPE_OBJECT, "GtkEntryCompletion",
156 &entry_completion_info, 0);
158 g_type_add_interface_static (entry_completion_type,
159 GTK_TYPE_CELL_LAYOUT,
163 return entry_completion_type;
167 gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
169 GObjectClass *object_class;
171 parent_class = g_type_class_peek_parent (klass);
172 object_class = (GObjectClass *)klass;
174 object_class->set_property = gtk_entry_completion_set_property;
175 object_class->get_property = gtk_entry_completion_get_property;
176 object_class->finalize = gtk_entry_completion_finalize;
179 * GtkEntryCompletion::match-selected:
180 * @widget: the object which received the signal
181 * @model: the #GtkTreeModel containing the matches
182 * @iter: a #GtkTreeIter positioned at the selected match
184 * The ::match-selected signal is emitted when a match from the list
185 * is selected. The default behaviour is to replace the contents of the
186 * entry with the contents of the text column in the row pointed to by
189 * Return value: %TRUE if the signal has been handled
191 entry_completion_signals[MATCH_SELECTED] =
192 g_signal_new ("match_selected",
193 G_TYPE_FROM_CLASS (klass),
195 G_STRUCT_OFFSET (GtkEntryCompletionClass, match_selected),
196 _gtk_boolean_handled_accumulator, NULL,
197 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
203 * GtkEntryCompletion::action-activated:
204 * @widget: the object which received the signal
205 * @index: the index of the activated action
207 * The ::action-activated signal is emitted when an action
210 entry_completion_signals[ACTION_ACTIVATED] =
211 g_signal_new ("action_activated",
212 G_TYPE_FROM_CLASS (klass),
214 G_STRUCT_OFFSET (GtkEntryCompletionClass, action_activated),
216 _gtk_marshal_NONE__INT,
220 g_object_class_install_property (object_class,
222 g_param_spec_object ("model",
223 P_("Completion Model"),
224 P_("The model to find matches in"),
227 g_object_class_install_property (object_class,
228 PROP_MINIMUM_KEY_LENGTH,
229 g_param_spec_int ("minimum_key_length",
230 P_("Minimum Key Length"),
231 P_("Minimum length of the search key in order to look up matches"),
237 g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
241 gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface)
243 iface->pack_start = gtk_entry_completion_pack_start;
244 iface->pack_end = gtk_entry_completion_pack_end;
245 iface->clear = gtk_entry_completion_clear;
246 iface->add_attribute = gtk_entry_completion_add_attribute;
247 iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func;
248 iface->clear_attributes = gtk_entry_completion_clear_attributes;
249 iface->reorder = gtk_entry_completion_reorder;
253 gtk_entry_completion_init (GtkEntryCompletion *completion)
255 GtkCellRenderer *cell;
256 GtkTreeSelection *sel;
257 GtkEntryCompletionPrivate *priv;
258 GtkWidget *popup_frame;
260 /* yes, also priv, need to keep the code readable */
261 priv = completion->priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion);
263 priv->minimum_key_length = 1;
264 priv->text_column = -1;
267 priv->filter_model = NULL;
269 priv->tree_view = gtk_tree_view_new ();
270 g_signal_connect (priv->tree_view, "button_press_event",
271 G_CALLBACK (gtk_entry_completion_list_button_press),
273 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
274 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
276 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
277 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
278 gtk_tree_selection_unselect_all (sel);
279 g_signal_connect (sel, "changed",
280 G_CALLBACK (gtk_entry_completion_selection_changed),
282 priv->first_sel_changed = TRUE;
284 priv->column = gtk_tree_view_column_new ();
285 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
287 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
288 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
290 GTK_POLICY_AUTOMATIC);
291 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
294 /* a nasty hack to get the completions treeview to size nicely */
295 gtk_widget_set_size_request (GTK_SCROLLED_WINDOW (priv->scrolled_window)->vscrollbar, -1, 0);
298 priv->actions = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
301 gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions));
302 g_signal_connect (priv->action_view, "button_press_event",
303 G_CALLBACK (gtk_entry_completion_action_button_press),
305 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->action_view), FALSE);
306 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->action_view), TRUE);
308 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->action_view));
309 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
310 gtk_tree_selection_unselect_all (sel);
312 cell = gtk_cell_renderer_text_new ();
313 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (priv->action_view),
316 gtk_entry_completion_action_data_func,
321 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
322 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
323 g_signal_connect (priv->popup_window, "key_press_event",
324 G_CALLBACK (gtk_entry_completion_popup_key_press),
326 g_signal_connect (priv->popup_window, "button_press_event",
327 G_CALLBACK (gtk_entry_completion_popup_button_press),
330 popup_frame = gtk_frame_new (NULL);
331 gtk_frame_set_shadow_type (GTK_FRAME (popup_frame),
332 GTK_SHADOW_ETCHED_IN);
333 gtk_widget_show (popup_frame);
334 gtk_container_add (GTK_CONTAINER (priv->popup_window), popup_frame);
336 priv->vbox = gtk_vbox_new (FALSE, 0);
337 gtk_container_add (GTK_CONTAINER (popup_frame), priv->vbox);
339 gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->tree_view);
340 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->scrolled_window,
343 /* we don't want to see the action treeview when no actions have
344 * been inserted, so we pack the action treeview after the first
345 * action has been added
350 gtk_entry_completion_set_property (GObject *object,
355 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
360 gtk_entry_completion_set_model (completion,
361 g_value_get_object (value));
364 case PROP_MINIMUM_KEY_LENGTH:
365 gtk_entry_completion_set_minimum_key_length (completion,
366 g_value_get_int (value));
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376 gtk_entry_completion_get_property (GObject *object,
381 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
386 g_value_set_object (value,
387 gtk_entry_completion_get_model (completion));
390 case PROP_MINIMUM_KEY_LENGTH:
391 g_value_set_int (value, gtk_entry_completion_get_minimum_key_length (completion));
395 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
401 gtk_entry_completion_finalize (GObject *object)
403 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object);
405 if (completion->priv->tree_view)
406 gtk_widget_destroy (completion->priv->tree_view);
408 if (completion->priv->entry)
409 gtk_entry_set_completion (GTK_ENTRY (completion->priv->entry), NULL);
411 if (completion->priv->actions)
412 g_object_unref (completion->priv->actions);
414 if (completion->priv->case_normalized_key)
415 g_free (completion->priv->case_normalized_key);
417 if (completion->priv->popup_window)
418 gtk_widget_destroy (completion->priv->popup_window);
420 G_OBJECT_CLASS (parent_class)->finalize (object);
423 /* implement cell layout interface */
425 gtk_entry_completion_pack_start (GtkCellLayout *cell_layout,
426 GtkCellRenderer *cell,
429 GtkEntryCompletionPrivate *priv;
431 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
433 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
435 gtk_tree_view_column_pack_start (priv->column, cell, expand);
439 gtk_entry_completion_pack_end (GtkCellLayout *cell_layout,
440 GtkCellRenderer *cell,
443 GtkEntryCompletionPrivate *priv;
445 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
447 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
449 gtk_tree_view_column_pack_end (priv->column, cell, expand);
453 gtk_entry_completion_clear (GtkCellLayout *cell_layout)
455 GtkEntryCompletionPrivate *priv;
457 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
459 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
461 gtk_tree_view_column_clear (priv->column);
465 gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout,
466 GtkCellRenderer *cell,
467 const gchar *attribute,
470 GtkEntryCompletionPrivate *priv;
472 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
474 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
476 gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column);
480 gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout,
481 GtkCellRenderer *cell,
482 GtkCellLayoutDataFunc func,
484 GDestroyNotify destroy)
486 GtkEntryCompletionPrivate *priv;
488 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
490 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
492 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column),
493 cell, func, func_data, destroy);
497 gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout,
498 GtkCellRenderer *cell)
500 GtkEntryCompletionPrivate *priv;
502 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
504 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
506 gtk_tree_view_column_clear_attributes (priv->column, cell);
510 gtk_entry_completion_reorder (GtkCellLayout *cell_layout,
511 GtkCellRenderer *cell,
514 GtkEntryCompletionPrivate *priv;
516 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout));
518 priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout);
520 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position);
523 /* all those callbacks */
525 gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion,
531 gchar *normalized_string;
532 gchar *case_normalized_string;
534 gboolean ret = FALSE;
538 model = gtk_tree_model_filter_get_model (completion->priv->filter_model);
540 gtk_tree_model_get (model, iter,
541 completion->priv->text_column, &item,
546 normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL);
547 case_normalized_string = g_utf8_casefold (normalized_string, -1);
549 if (!strncmp (key, case_normalized_string, strlen (key)))
553 g_free (normalized_string);
554 g_free (case_normalized_string);
561 gtk_entry_completion_visible_func (GtkTreeModel *model,
565 gboolean ret = FALSE;
567 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
569 if (!completion->priv->case_normalized_key)
572 if (completion->priv->match_func)
573 ret = (* completion->priv->match_func) (completion,
574 completion->priv->case_normalized_key,
576 completion->priv->match_data);
577 else if (completion->priv->text_column >= 0)
578 ret = gtk_entry_completion_default_completion_func (completion,
579 completion->priv->case_normalized_key,
587 gtk_entry_completion_popup_key_press (GtkWidget *widget,
591 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
593 if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
596 /* propagate event to the entry */
597 gtk_widget_event (completion->priv->entry, (GdkEvent *)event);
603 gtk_entry_completion_popup_button_press (GtkWidget *widget,
604 GdkEventButton *event,
607 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
609 if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
612 /* if we come here, it's usually time to popdown */
613 _gtk_entry_completion_popdown (completion);
619 gtk_entry_completion_list_button_press (GtkWidget *widget,
620 GdkEventButton *event,
623 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
624 GtkTreePath *path = NULL;
626 if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
629 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
631 &path, NULL, NULL, NULL))
636 gtk_tree_model_get_iter (GTK_TREE_MODEL (completion->priv->filter_model),
638 gtk_tree_path_free (path);
640 g_signal_handler_block (completion->priv->entry,
641 completion->priv->changed_id);
642 g_signal_emit (completion, entry_completion_signals[MATCH_SELECTED],
643 0, GTK_TREE_MODEL (completion->priv->filter_model),
645 g_signal_handler_unblock (completion->priv->entry,
646 completion->priv->changed_id);
652 gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->filter_model),
654 completion->priv->text_column, &str,
657 g_signal_handler_block (completion->priv->entry,
658 completion->priv->changed_id);
659 gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str);
660 g_signal_handler_unblock (completion->priv->entry,
661 completion->priv->changed_id);
663 /* move cursor to the end */
664 gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry),
670 _gtk_entry_completion_popdown (completion);
678 gtk_entry_completion_action_button_press (GtkWidget *widget,
679 GdkEventButton *event,
682 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
683 GtkTreePath *path = NULL;
685 if (!GTK_WIDGET_MAPPED (completion->priv->popup_window))
688 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
690 &path, NULL, NULL, NULL))
692 g_signal_emit (completion, entry_completion_signals[ACTION_ACTIVATED],
693 0, gtk_tree_path_get_indices (path)[0]);
694 gtk_tree_path_free (path);
696 _gtk_entry_completion_popdown (completion);
704 gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column,
705 GtkCellRenderer *cell,
710 gchar *string = NULL;
713 gtk_tree_model_get (model, iter,
722 g_object_set (G_OBJECT (cell),
727 g_object_set (G_OBJECT (cell),
736 gtk_entry_completion_selection_changed (GtkTreeSelection *selection,
739 GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
741 if (completion->priv->first_sel_changed)
743 completion->priv->first_sel_changed = FALSE;
744 if (gtk_widget_is_focus (completion->priv->tree_view))
745 gtk_tree_selection_unselect_all (selection);
752 * gtk_entry_completion_new:
754 * Creates a new #GtkEntryCompletion object.
756 * Return value: A newly created #GtkEntryCompletion object.
761 gtk_entry_completion_new (void)
763 GtkEntryCompletion *completion;
765 completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION, NULL);
771 * gtk_entry_completion_get_entry:
772 * @completion: A #GtkEntryCompletion.
774 * Gets the entry @completion has been attached to.
776 * Return value: The entry @completion has been attached to.
781 gtk_entry_completion_get_entry (GtkEntryCompletion *completion)
783 g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
785 return completion->priv->entry;
789 * gtk_entry_completion_set_model:
790 * @completion: A #GtkEntryCompletion.
791 * @model: The #GtkTreeModel.
793 * Sets the model for a #GtkEntryCompletion. If @completion already has
794 * a model set, it will remove it before setting the new model.
799 gtk_entry_completion_set_model (GtkEntryCompletion *completion,
802 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
803 g_return_if_fail (GTK_IS_TREE_MODEL (model));
805 /* code will unref the old filter model (if any) */
806 completion->priv->filter_model =
807 GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
808 gtk_tree_model_filter_set_visible_func (completion->priv->filter_model,
809 gtk_entry_completion_visible_func,
812 gtk_tree_view_set_model (GTK_TREE_VIEW (completion->priv->tree_view),
813 GTK_TREE_MODEL (completion->priv->filter_model));
814 g_object_unref (G_OBJECT (completion->priv->filter_model));
818 * gtk_entry_completion_get_model:
819 * @completion: A #GtkEntryCompletion.
821 * Returns the model the #GtkEntryCompletion is using as data source.
822 * Returns %NULL if the model is unset.
824 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
829 gtk_entry_completion_get_model (GtkEntryCompletion *completion)
831 g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL);
833 return gtk_tree_model_filter_get_model (completion->priv->filter_model);
837 * gtk_entry_completion_set_match_func:
838 * @completion: A #GtkEntryCompletion.
839 * @func: The #GtkEntryCompletionMatchFunc to use.
840 * @func_data: The user data for @func.
841 * @func_notify: Destroy notifier for @func_data.
843 * Sets the match function for @completion to be @func. The match function
844 * is used to determine if a row should or should not be in the completion
850 gtk_entry_completion_set_match_func (GtkEntryCompletion *completion,
851 GtkEntryCompletionMatchFunc func,
853 GDestroyNotify func_notify)
855 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
857 if (completion->priv->match_notify)
858 (* completion->priv->match_notify) (completion->priv->match_data);
860 completion->priv->match_func = func;
861 completion->priv->match_data = func_data;
862 completion->priv->match_notify = func_notify;
866 * gtk_entry_completion_set_minimum_key_length:
867 * @completion: A #GtkEntryCompletion.
868 * @length: The minimum length of the key in order to start completing.
870 * Requires the length of the search key for @completion to be at least
871 * @length. This is useful for long lists, where completing using a small
872 * key takes a lot of time and will come up with meaningless results anyway
873 * (ie, a too large dataset).
878 gtk_entry_completion_set_minimum_key_length (GtkEntryCompletion *completion,
881 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
882 g_return_if_fail (length >= 1);
884 completion->priv->minimum_key_length = length;
888 * gtk_entry_completion_get_minimum_key_length:
889 * @completion: A #GtkEntryCompletion.
891 * Returns the minimum key length as set for @completion.
893 * Return value: The currently used minimum key length.
898 gtk_entry_completion_get_minimum_key_length (GtkEntryCompletion *completion)
900 g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
902 return completion->priv->minimum_key_length;
906 * gtk_entry_completion_complete:
907 * @completion: A #GtkEntryCompletion.
909 * Requests a completion operation, or in other words a refiltering of the
910 * current list with completions, using the current key. The completion list
911 * view will be updated accordingly.
916 gtk_entry_completion_complete (GtkEntryCompletion *completion)
920 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
921 g_return_if_fail (completion->priv->filter_model != NULL);
923 if (completion->priv->case_normalized_key)
924 g_free (completion->priv->case_normalized_key);
926 tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)),
927 -1, G_NORMALIZE_ALL);
928 completion->priv->case_normalized_key = g_utf8_casefold (tmp, -1);
931 gtk_tree_model_filter_refilter (completion->priv->filter_model);
935 gtk_entry_completion_insert_action (GtkEntryCompletion *completion,
942 gtk_list_store_insert (completion->priv->actions, &iter, index);
943 gtk_list_store_set (completion->priv->actions, &iter,
948 if (!completion->priv->action_view->parent)
950 GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
952 gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
954 gtk_tree_path_free (path);
956 gtk_box_pack_start (GTK_BOX (completion->priv->vbox),
957 completion->priv->action_view, FALSE, FALSE, 0);
958 gtk_widget_show (completion->priv->action_view);
963 * gtk_entry_completion_insert_action_text:
964 * @completion: A #GtkEntryCompletion.
965 * @index: The index of the item to insert.
966 * @text: Text of the item to insert.
968 * Inserts an action in @completion's action item list at position @index
969 * with text @text. If you want the action item to have markup, use
970 * gtk_entry_completion_insert_action_markup().
975 gtk_entry_completion_insert_action_text (GtkEntryCompletion *completion,
979 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
980 g_return_if_fail (text != NULL);
982 gtk_entry_completion_insert_action (completion, index, text, FALSE);
986 * gtk_entry_completion_insert_action_markup:
987 * @completion: A #GtkEntryCompletion.
988 * @index: The index of the item to insert.
989 * @markup: Markup of the item to insert.
991 * Inserts an action in @completion's action item list at position @index
992 * with markup @markup.
997 gtk_entry_completion_insert_action_markup (GtkEntryCompletion *completion,
1001 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1002 g_return_if_fail (markup != NULL);
1004 gtk_entry_completion_insert_action (completion, index, markup, TRUE);
1008 * gtk_entry_completion_delete_action:
1009 * @completion: A #GtkEntryCompletion.
1010 * @index: The index of the item to Delete.
1012 * Deletes the action at @index from @completion's action list.
1017 gtk_entry_completion_delete_action (GtkEntryCompletion *completion,
1022 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1023 g_return_if_fail (index >= 0);
1025 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (completion->priv->actions),
1026 &iter, NULL, index);
1027 gtk_list_store_remove (completion->priv->actions, &iter);
1031 * gtk_entry_completion_set_text_column:
1032 * @completion: A #GtkEntryCompletion.
1033 * @column: The column in the model of @completion to get strings from.
1035 * Convenience function for setting up the most used case of this code: a
1036 * completion list with just strings. This function will set up @completion
1037 * to have a list displaying all (and just) strings in the completion list,
1038 * and to get those strings from @column in the model of @completion.
1040 * This functions creates and adds a GtkCellRendererText for the selected column.
1045 gtk_entry_completion_set_text_column (GtkEntryCompletion *completion,
1048 GtkCellRenderer *cell;
1050 g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
1051 g_return_if_fail (column >= 0);
1053 completion->priv->text_column = column;
1055 cell = gtk_cell_renderer_text_new ();
1056 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1058 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1065 /* lame copy from gtkentry.c */
1067 get_borders (GtkEntry *entry,
1071 GtkWidget *widget = GTK_WIDGET (entry);
1073 gboolean interior_focus;
1075 gtk_widget_style_get (widget,
1076 "interior-focus", &interior_focus,
1077 "focus-line-width", &focus_width,
1080 if (entry->has_frame)
1082 *xborder = widget->style->xthickness;
1083 *yborder = widget->style->ythickness;
1091 if (!interior_focus)
1093 *xborder += focus_width;
1094 *yborder += focus_width;
1098 /* some nasty size requisition */
1100 _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
1103 gint matches, items, height, x_border, y_border;
1106 GdkRectangle monitor;
1107 GtkRequisition popup_req;
1111 gdk_window_get_origin (completion->priv->entry->window, &x, &y);
1112 get_borders (GTK_ENTRY (completion->priv->entry), &x_border, &y_border);
1117 matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
1119 items = MIN (matches, 15);
1121 gtk_tree_view_column_cell_get_size (completion->priv->column, NULL,
1122 NULL, NULL, NULL, &height);
1125 gtk_widget_hide (completion->priv->scrolled_window);
1127 gtk_widget_show (completion->priv->scrolled_window);
1129 gtk_widget_set_size_request (completion->priv->tree_view,
1130 completion->priv->entry->allocation.width - 2 * x_border,
1133 /* default on no match */
1134 completion->priv->current_selected = -1;
1136 items = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
1140 gtk_widget_show (completion->priv->action_view);
1142 gtk_tree_view_column_cell_get_size (gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0),
1143 NULL, NULL, NULL, NULL,
1146 gtk_widget_set_size_request (completion->priv->action_view,
1147 completion->priv->entry->allocation.width - 2 * x_border,
1151 gtk_widget_hide (completion->priv->action_view);
1153 gtk_widget_size_request (completion->priv->popup_window, &popup_req);
1155 screen = gtk_widget_get_screen (GTK_WIDGET (completion->priv->entry));
1156 monitor_num = gdk_screen_get_monitor_at_window (screen,
1157 GTK_WIDGET (completion->priv->entry)->window);
1158 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1162 else if (x + popup_req.width > monitor.x + monitor.width)
1163 x = monitor.x + monitor.width - popup_req.width;
1165 if (y + height + popup_req.height <= monitor.y + monitor.height)
1172 y -= popup_req.height;
1178 path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
1179 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view), path,
1180 NULL, FALSE, 0.0, 0.0);
1181 gtk_tree_path_free (path);
1184 gtk_window_move (GTK_WINDOW (completion->priv->popup_window), x, y);
1190 _gtk_entry_completion_popup (GtkEntryCompletion *completion)
1192 GtkTreeViewColumn *column;
1195 if (GTK_WIDGET_MAPPED (completion->priv->popup_window))
1198 completion->priv->may_wrap = TRUE;
1200 column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
1201 renderers = gtk_tree_view_column_get_cell_renderers (column);
1202 gtk_widget_ensure_style (completion->priv->tree_view);
1203 g_object_set (GTK_CELL_RENDERER (renderers->data), "cell_background_gdk",
1204 &completion->priv->tree_view->style->bg[GTK_STATE_NORMAL],
1206 g_list_free (renderers);
1208 gtk_widget_show_all (completion->priv->vbox);
1210 _gtk_entry_completion_resize_popup (completion);
1212 gtk_widget_show (completion->priv->popup_window);
1214 gtk_grab_add (completion->priv->popup_window);
1215 gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
1216 GDK_BUTTON_PRESS_MASK |
1217 GDK_BUTTON_RELEASE_MASK |
1218 GDK_POINTER_MOTION_MASK,
1219 NULL, NULL, GDK_CURRENT_TIME);
1223 _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
1225 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1226 gtk_grab_remove (completion->priv->popup_window);
1228 gtk_widget_hide (completion->priv->popup_window);