X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkentrycompletion.c;h=2fc582ecdf397c971f6a8702f782fd4f1ff002ea;hb=0e3d5fb732ab046943435c04d035ed316fd6c6d4;hp=2c327153ae8cf5eaa3cab0fe5eb32c1c3e8465c9;hpb=90196d7e5ea063b604c493b1d1248e1c4683e5bc;p=~andy%2Fgtk diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c index 2c327153a..2fc582ecd 100644 --- a/gtk/gtkentrycompletion.c +++ b/gtk/gtkentrycompletion.c @@ -32,7 +32,6 @@ #include "gtkwindow.h" #include "gtkentry.h" #include "gtkmain.h" -#include "gtksignal.h" #include "gtkmarshalers.h" #include "gtkprivate.h" @@ -47,6 +46,7 @@ enum INSERT_PREFIX, MATCH_SELECTED, ACTION_ACTIVATED, + CURSOR_ON_MATCH, LAST_SIGNAL }; @@ -60,134 +60,110 @@ enum PROP_INLINE_COMPLETION, PROP_POPUP_COMPLETION, PROP_POPUP_SET_WIDTH, - PROP_POPUP_SINGLE_MATCH + PROP_POPUP_SINGLE_MATCH, + PROP_INLINE_SELECTION }; #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate)) -static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass); -static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface); -static void gtk_entry_completion_init (GtkEntryCompletion *completion); -static void gtk_entry_completion_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_entry_completion_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void gtk_entry_completion_finalize (GObject *object); - -static void gtk_entry_completion_pack_start (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void gtk_entry_completion_pack_end (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gboolean expand); -static void gtk_entry_completion_clear (GtkCellLayout *cell_layout); -static void gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - const char *attribute, - gint column); -static void gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkCellLayoutDataFunc func, - gpointer func_data, - GDestroyNotify destroy); -static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout, - GtkCellRenderer *cell); -static void gtk_entry_completion_reorder (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - gint position); - -static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data); -static gboolean gtk_entry_completion_popup_key_press (GtkWidget *widget, - GdkEventKey *event, - gpointer user_data); -static gboolean gtk_entry_completion_popup_button_press (GtkWidget *widget, - GdkEventButton *event, - gpointer user_data); -static gboolean gtk_entry_completion_list_button_press (GtkWidget *widget, - GdkEventButton *event, - gpointer user_data); -static gboolean gtk_entry_completion_action_button_press (GtkWidget *widget, - GdkEventButton *event, - gpointer user_data); -static void gtk_entry_completion_selection_changed (GtkTreeSelection *selection, - gpointer data); -static gboolean gtk_entry_completion_list_enter_notify (GtkWidget *widget, - GdkEventCrossing *event, - gpointer data); -static gboolean gtk_entry_completion_list_motion_notify (GtkWidget *widget, - GdkEventMotion *event, - gpointer data); -static void gtk_entry_completion_insert_action (GtkEntryCompletion *completion, - gint index, - const gchar *string, - gboolean markup); -static void gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data); +static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface); +static void gtk_entry_completion_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_entry_completion_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_entry_completion_finalize (GObject *object); + +static void gtk_entry_completion_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +static void gtk_entry_completion_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand); +static void gtk_entry_completion_clear (GtkCellLayout *cell_layout); +static void gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const char *attribute, + gint column); +static void gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy); +static void gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell); +static void gtk_entry_completion_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gint position); +static GList * gtk_entry_completion_get_cells (GtkCellLayout *cell_layout); + +static gboolean gtk_entry_completion_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); +static gboolean gtk_entry_completion_popup_key_event (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data); +static gboolean gtk_entry_completion_popup_button_press (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data); +static gboolean gtk_entry_completion_list_button_press (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data); +static gboolean gtk_entry_completion_action_button_press (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data); +static void gtk_entry_completion_selection_changed (GtkTreeSelection *selection, + gpointer data); +static gboolean gtk_entry_completion_list_enter_notify (GtkWidget *widget, + GdkEventCrossing *event, + gpointer data); +static gboolean gtk_entry_completion_list_motion_notify (GtkWidget *widget, + GdkEventMotion *event, + gpointer data); +static void gtk_entry_completion_insert_action (GtkEntryCompletion *completion, + gint index, + const gchar *string, + gboolean markup); +static void gtk_entry_completion_action_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); static gboolean gtk_entry_completion_match_selected (GtkEntryCompletion *completion, GtkTreeModel *model, GtkTreeIter *iter); static gboolean gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, const gchar *prefix); +static gboolean gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean gtk_entry_completion_insert_completion (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter); +static void gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion, + const gchar *text); -static GObjectClass *parent_class = NULL; static guint entry_completion_signals[LAST_SIGNAL] = { 0 }; +/* GtkBuildable */ +static void gtk_entry_completion_buildable_init (GtkBuildableIface *iface); -GType -gtk_entry_completion_get_type (void) -{ - static GType entry_completion_type = 0; +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)) - if (!entry_completion_type) - { - static const GTypeInfo entry_completion_info = - { - sizeof (GtkEntryCompletionClass), - NULL, - NULL, - (GClassInitFunc) gtk_entry_completion_class_init, - NULL, - NULL, - sizeof (GtkEntryCompletion), - 0, - (GInstanceInitFunc) gtk_entry_completion_init - }; - - static const GInterfaceInfo cell_layout_info = - { - (GInterfaceInitFunc) gtk_entry_completion_cell_layout_init, - NULL, - NULL - }; - - entry_completion_type = - g_type_register_static (G_TYPE_OBJECT, I_("GtkEntryCompletion"), - &entry_completion_info, 0); - - g_type_add_interface_static (entry_completion_type, - GTK_TYPE_CELL_LAYOUT, - &cell_layout_info); - } - - return entry_completion_type; -} static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) { GObjectClass *object_class; - parent_class = g_type_class_peek_parent (klass); object_class = (GObjectClass *)klass; object_class->set_property = gtk_entry_completion_set_property; @@ -196,6 +172,7 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) klass->match_selected = gtk_entry_completion_match_selected; klass->insert_prefix = gtk_entry_completion_real_insert_prefix; + klass->cursor_on_match = gtk_entry_completion_cursor_on_match; /** * GtkEntryCompletion::insert-prefix: @@ -250,6 +227,32 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) G_TYPE_BOOLEAN, 2, GTK_TYPE_TREE_MODEL, GTK_TYPE_TREE_ITER); + /** + * GtkEntryCompletion::cursor-on-match: + * @widget: the object which received the signal + * @model: the #GtkTreeModel containing the matches + * @iter: a #GtkTreeIter positioned at the selected match + * + * Gets emitted when a match from the cursor is on a match + * of the list.The default behaviour is to replace the contents + * of the entry with the contents of the text column in the row + * pointed to by @iter. + * + * Return value: %TRUE if the signal has been handled + * + * Since: 2.12 + */ + + entry_completion_signals[CURSOR_ON_MATCH] = + g_signal_new (I_("cursor_on_match"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkEntryCompletionClass, cursor_on_match), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__OBJECT_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_MODEL, + GTK_TYPE_TREE_ITER); /** * GtkEntryCompletion::action-activated: @@ -290,6 +293,7 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) * GtkEntryCompletion:text-column: * * The column of the model containing the strings. + * Note that the strings must be UTF-8. * * Since: 2.6 */ @@ -370,10 +374,33 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) P_("If TRUE, the popup window will appear for a single match."), TRUE, GTK_PARAM_READWRITE)); + /** + * GtkEntryCompletion:inline-selection: + * + * Determines whether the possible completions on the popup + * will appear in the entry as you navigate through them. + + * Since: 2.12 + */ + g_object_class_install_property (object_class, + PROP_INLINE_SELECTION, + g_param_spec_boolean ("inline-selection", + P_("Inline selection"), + P_("Your description here"), + FALSE, + GTK_PARAM_READWRITE)); g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate)); } +static void +gtk_entry_completion_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = _gtk_cell_layout_buildable_add_child; + iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; + iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end; +} + static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface) { @@ -384,6 +411,7 @@ gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface) iface->set_cell_data_func = gtk_entry_completion_set_cell_data_func; iface->clear_attributes = gtk_entry_completion_clear_attributes; iface->reorder = gtk_entry_completion_reorder; + iface->get_cells = gtk_entry_completion_get_cells; } static void @@ -404,6 +432,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) priv->popup_completion = TRUE; priv->popup_set_width = TRUE; priv->popup_single_match = TRUE; + priv->inline_selection = FALSE; /* completions */ priv->filter_model = NULL; @@ -418,6 +447,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) g_signal_connect (priv->tree_view, "motion_notify_event", G_CALLBACK (gtk_entry_completion_list_motion_notify), completion); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE); gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE); @@ -447,6 +477,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) priv->action_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->actions)); + g_object_ref_sink (priv->action_view); g_signal_connect (priv->action_view, "button_press_event", G_CALLBACK (gtk_entry_completion_action_button_press), completion); @@ -474,8 +505,12 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) /* pack it all */ priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE); + gtk_window_set_type_hint(GTK_WINDOW(priv->popup_window), GDK_WINDOW_TYPE_HINT_COMBO); g_signal_connect (priv->popup_window, "key_press_event", - G_CALLBACK (gtk_entry_completion_popup_key_press), + G_CALLBACK (gtk_entry_completion_popup_key_event), + completion); + g_signal_connect (priv->popup_window, "key_release_event", + G_CALLBACK (gtk_entry_completion_popup_key_event), completion); g_signal_connect (priv->popup_window, "button_press_event", G_CALLBACK (gtk_entry_completion_popup_button_press), @@ -507,7 +542,7 @@ gtk_entry_completion_set_property (GObject *object, GParamSpec *pspec) { GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object); - GtkEntryCompletionPrivate *priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (completion); + GtkEntryCompletionPrivate *priv = completion->priv; switch (prop_id) { @@ -541,6 +576,10 @@ gtk_entry_completion_set_property (GObject *object, priv->popup_single_match = g_value_get_boolean (value); break; + case PROP_INLINE_SELECTION: + priv->inline_selection = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -586,6 +625,10 @@ gtk_entry_completion_get_property (GObject *object, g_value_set_boolean (value, gtk_entry_completion_get_popup_single_match (completion)); break; + case PROP_INLINE_SELECTION: + g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -596,23 +639,29 @@ static void gtk_entry_completion_finalize (GObject *object) { GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (object); + GtkEntryCompletionPrivate *priv = completion->priv; + + if (priv->tree_view) + gtk_widget_destroy (priv->tree_view); - if (completion->priv->tree_view) - gtk_widget_destroy (completion->priv->tree_view); + if (priv->entry) + gtk_entry_set_completion (GTK_ENTRY (priv->entry), NULL); - if (completion->priv->entry) - gtk_entry_set_completion (GTK_ENTRY (completion->priv->entry), NULL); + if (priv->actions) + g_object_unref (priv->actions); + if (priv->action_view) + g_object_unref (priv->action_view); - if (completion->priv->actions) - g_object_unref (completion->priv->actions); + g_free (priv->case_normalized_key); + g_free (priv->completion_prefix); - if (completion->priv->case_normalized_key) - g_free (completion->priv->case_normalized_key); + if (priv->popup_window) + gtk_widget_destroy (priv->popup_window); - if (completion->priv->popup_window) - gtk_widget_destroy (completion->priv->popup_window); + if (priv->match_notify) + (* priv->match_notify) (priv->match_data); - G_OBJECT_CLASS (parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_entry_completion_parent_class)->finalize (object); } /* implement cell layout interface */ @@ -623,8 +672,6 @@ gtk_entry_completion_pack_start (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_tree_view_column_pack_start (priv->column, cell, expand); @@ -637,8 +684,6 @@ gtk_entry_completion_pack_end (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_tree_view_column_pack_end (priv->column, cell, expand); @@ -649,8 +694,6 @@ gtk_entry_completion_clear (GtkCellLayout *cell_layout) { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_tree_view_column_clear (priv->column); @@ -664,8 +707,6 @@ gtk_entry_completion_add_attribute (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_tree_view_column_add_attribute (priv->column, cell, attribute, column); @@ -680,8 +721,6 @@ gtk_entry_completion_set_cell_data_func (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), @@ -694,8 +733,6 @@ gtk_entry_completion_clear_attributes (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_tree_view_column_clear_attributes (priv->column, cell); @@ -708,13 +745,21 @@ gtk_entry_completion_reorder (GtkCellLayout *cell_layout, { GtkEntryCompletionPrivate *priv; - g_return_if_fail (GTK_IS_ENTRY_COMPLETION (cell_layout)); - priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column), cell, position); } +static GList * +gtk_entry_completion_get_cells (GtkCellLayout *cell_layout) +{ + GtkEntryCompletionPrivate *priv; + + priv = GTK_ENTRY_COMPLETION_GET_PRIVATE (cell_layout); + + return gtk_tree_view_column_get_cell_renderers (priv->column); +} + /* all those callbacks */ static gboolean gtk_entry_completion_default_completion_func (GtkEntryCompletion *completion, @@ -782,7 +827,7 @@ gtk_entry_completion_visible_func (GtkTreeModel *model, } static gboolean -gtk_entry_completion_popup_key_press (GtkWidget *widget, +gtk_entry_completion_popup_key_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { @@ -862,6 +907,8 @@ gtk_entry_completion_action_button_press (GtkWidget *widget, if (!GTK_WIDGET_MAPPED (completion->priv->popup_window)) return FALSE; + _gtk_entry_reset_im_context (GTK_ENTRY (completion->priv->entry)); + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL)) @@ -1040,7 +1087,7 @@ gtk_entry_completion_get_model (GtkEntryCompletion *completion) * is used to determine if a row should or should not be in the completion * list. * - * Since: 2.4. + * Since: 2.4 */ void gtk_entry_completion_set_match_func (GtkEntryCompletion *completion, @@ -1123,8 +1170,7 @@ gtk_entry_completion_complete (GtkEntryCompletion *completion) if (!completion->priv->filter_model) return; - if (completion->priv->case_normalized_key) - g_free (completion->priv->case_normalized_key); + g_free (completion->priv->case_normalized_key); tmp = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)), -1, G_NORMALIZE_ALL); @@ -1307,7 +1353,7 @@ gtk_entry_completion_list_motion_notify (GtkWidget *widget, { GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data); - completion->priv->ignore_enter = FALSE; + completion->priv->ignore_enter = FALSE; return FALSE; } @@ -1347,6 +1393,8 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion) NULL); height += vertical_separator; + + gtk_widget_realize (completion->priv->tree_view); if (items <= 0) gtk_widget_hide (completion->priv->scrolled_window); @@ -1387,7 +1435,8 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion) else if (x + popup_req.width > monitor.x + monitor.width) x = monitor.x + monitor.width - popup_req.width; - if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height) + if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height || + y - monitor.y < (monitor.y + monitor.height) - (y + entry_req.height)) { y += entry_req.height; above = FALSE; @@ -1416,6 +1465,7 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion) { GtkTreeViewColumn *column; GList *renderers; + GtkWidget *toplevel; if (GTK_WIDGET_MAPPED (completion->priv->popup_window)) return; @@ -1423,6 +1473,9 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion) if (!GTK_WIDGET_MAPPED (completion->priv->entry)) return; + if (!GTK_WIDGET_HAS_FOCUS (completion->priv->entry)) + return; + completion->priv->ignore_enter = TRUE; column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0); @@ -1437,6 +1490,18 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion) _gtk_entry_completion_resize_popup (completion); + toplevel = gtk_widget_get_toplevel (completion->priv->entry); + if (GTK_IS_WINDOW (toplevel)) + gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), + GTK_WINDOW (completion->priv->popup_window)); + + /* prevent the first row being focused */ + gtk_widget_grab_focus (completion->priv->tree_view); + + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view))); + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view))); + + gtk_widget_show (completion->priv->popup_window); gtk_grab_add (completion->priv->popup_window); @@ -1469,7 +1534,7 @@ gtk_entry_completion_match_selected (GtkEntryCompletion *completion, gchar *str = NULL; gtk_tree_model_get (model, iter, completion->priv->text_column, &str, -1); - gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str); + gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), str ? str : ""); /* move cursor to the end */ gtk_editable_set_position (GTK_EDITABLE (completion->priv->entry), -1); @@ -1479,6 +1544,15 @@ gtk_entry_completion_match_selected (GtkEntryCompletion *completion, return TRUE; } +static gboolean +gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + gtk_entry_completion_insert_completion (completion, model, iter); + + return TRUE; +} static gchar * gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion) @@ -1504,14 +1578,14 @@ gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion) &iter, completion->priv->text_column, &text, -1); - if (g_str_has_prefix (text, key)) + if (text && g_str_has_prefix (text, key)) { if (!prefix) prefix = g_strdup (text); else { gchar *p = prefix; - const gchar *q = text; + gchar *q = text; while (*p && *p == *q) { @@ -1520,6 +1594,19 @@ gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion) } *p = '\0'; + + if (p > prefix) + { + /* strip a partial multibyte character */ + q = g_utf8_find_prev_char (prefix, p); + switch (g_utf8_get_char_validated (q, p - q)) + { + case (gunichar)-2: + case (gunichar)-1: + *q = 0; + default: ; + } + } } } @@ -1563,6 +1650,73 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, return TRUE; } +/** + * gtk_entry_completion_get_completion_prefix: + * @completion: a #GtkEntryCompletion + * + * Get the original text entered by the user that triggered + * the completion or %NULL if there's no completion ongoing. + * + * Returns: the prefix for the current completion + * + * Since: 2.12 + **/ +const gchar* +gtk_entry_completion_get_completion_prefix (GtkEntryCompletion *completion) +{ + g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), NULL); + + return completion->priv->completion_prefix; +} + +static void +gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion, + const gchar *text) +{ + GtkEntryCompletionPrivate *priv = completion->priv; + gint len; + + priv = completion->priv; + + if (priv->changed_id > 0) + g_signal_handler_block (priv->entry, priv->changed_id); + + if (priv->insert_text_id > 0) + g_signal_handler_block (priv->entry, priv->insert_text_id); + + gtk_entry_set_text (GTK_ENTRY (priv->entry), text); + + len = strlen (priv->completion_prefix); + gtk_editable_select_region (GTK_EDITABLE (priv->entry), len, -1); + + if (priv->changed_id > 0) + g_signal_handler_unblock (priv->entry, priv->changed_id); + + if (priv->insert_text_id > 0) + g_signal_handler_unblock (priv->entry, priv->insert_text_id); +} + +static gboolean +gtk_entry_completion_insert_completion (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + gchar *str = NULL; + + if (completion->priv->text_column < 0) + return FALSE; + + gtk_tree_model_get (model, iter, + completion->priv->text_column, &str, + -1); + + gtk_entry_completion_insert_completion_text (completion, str); + + g_free (str); + + return TRUE; +} + /** * gtk_entry_completion_insert_prefix: * @completion: a #GtkEntryCompletion @@ -1571,14 +1725,17 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, * * Since: 2.6 **/ + void gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion) { gboolean done; gchar *prefix; - g_signal_handler_block (completion->priv->entry, - completion->priv->insert_text_id); + if (completion->priv->insert_text_id > 0) + g_signal_handler_block (completion->priv->entry, + completion->priv->insert_text_id); + prefix = gtk_entry_completion_compute_prefix (completion); if (prefix) { @@ -1586,8 +1743,10 @@ gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion) 0, prefix, &done); g_free (prefix); } - g_signal_handler_unblock (completion->priv->entry, - completion->priv->insert_text_id); + + if (completion->priv->insert_text_id > 0) + g_signal_handler_unblock (completion->priv->entry, + completion->priv->insert_text_id); } /** @@ -1616,7 +1775,6 @@ gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion, } } - /** * gtk_entry_completion_get_inline_completion: * @completion: a #GtkEntryCompletion @@ -1776,6 +1934,49 @@ gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion) return completion->priv->popup_single_match; } +/** + * gtk_entry_completion_set_inline_selection: + * @completion: a #GtkEntryCompletion + * @inline_selection: %TRUE to do inline selection + * + * Sets whether it is possible to cycle through the possible completions + * inside the entry. + * + * Since: 2.12 + **/ +void +gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion, + gboolean inline_selection) +{ + g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion)); + + inline_selection = inline_selection != FALSE; + + if (completion->priv->inline_selection != inline_selection) + { + completion->priv->inline_selection = inline_selection; + + g_object_notify (G_OBJECT (completion), "inline-selection"); + } +} + +/** + * gtk_entry_completion_get_inline_selection: + * @completion: a #GtkEntryCompletion + * + * Returns %TRUE if inline-selection mode is turned on. + * + * Returns: %TRUE if inline-selection mode is on + * + * Since: 2.12 + **/ +gboolean +gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion) +{ + g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE); + + return completion->priv->inline_selection; +} #define __GTK_ENTRY_COMPLETION_C__ #include "gtkaliasdef.c"