diff options
Diffstat (limited to 'src/timezone-completion.c')
-rw-r--r-- | src/timezone-completion.c | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/src/timezone-completion.c b/src/timezone-completion.c index 1c47bc0..ad04d0c 100644 --- a/src/timezone-completion.c +++ b/src/timezone-completion.c @@ -24,6 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <json-glib/json-glib.h> #include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> #include <glib/gi18n.h> #include "timezone-completion.h" #include "tz.h" @@ -41,6 +42,7 @@ struct _TimezoneCompletionPrivate GtkEntry * entry; guint queued_request; guint changed_id; + guint keypress_id; GCancellable * cancel; gchar * request_text; GHashTable * request_table; @@ -316,6 +318,7 @@ entry_changed (GtkEntry * entry, TimezoneCompletion * completion) if (priv->queued_request) { g_source_remove (priv->queued_request); + priv->queued_request = 0; } /* See if we've already got this one */ @@ -331,6 +334,94 @@ entry_changed (GtkEntry * entry, TimezoneCompletion * completion) gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (completion)); } +static GtkWidget * +get_descendent (GtkWidget * parent, GType type) +{ + if (g_type_is_a (G_OBJECT_TYPE (parent), type)) + return parent; + + if (GTK_IS_CONTAINER (parent)) { + GList * children = gtk_container_get_children (GTK_CONTAINER (parent)); + GList * iter; + for (iter = children; iter; iter = iter->next) { + GtkWidget * found = get_descendent (GTK_WIDGET (iter->data), type); + if (found) { + g_list_free (children); + return found; + } + } + g_list_free (children); + } + + return NULL; +} + +/** + * The popup window and its GtkTreeView are private to our parent completion + * object. We can't get access to discover if there is a highlighted item or + * even if the window is showing right now. So this is a super hack to find + * it by looking through our toplevel's window group and finding a window with + * a GtkTreeView that points at our model. There should be only one ever, so + * we'll use the first one we find. + */ +static GtkTreeView * +find_popup_treeview (GtkWidget * widget, GtkTreeModel * model) +{ + GtkWidget * toplevel = gtk_widget_get_toplevel (widget); + if (!GTK_IS_WINDOW (toplevel)) + return NULL; + + GtkWindowGroup * group = gtk_window_get_group (GTK_WINDOW (toplevel)); + GList * windows = gtk_window_group_list_windows (group); + GList * iter; + for (iter = windows; iter; iter = iter->next) { + if (iter->data == toplevel) + continue; // Skip our own window, we don't have it + GtkWidget * view = get_descendent (GTK_WIDGET (iter->data), GTK_TYPE_TREE_VIEW); + if (view != NULL) { + GtkTreeModel * tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + if (GTK_IS_TREE_MODEL_FILTER (tree_model)) + tree_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (tree_model)); + if (tree_model == model) { + g_list_free (windows); + return GTK_TREE_VIEW (view); + } + } + } + g_list_free (windows); + + return NULL; +} + +static gboolean +entry_keypress (GtkEntry * entry, GdkEventKey *event, TimezoneCompletion * completion) +{ + if (event->keyval == GDK_ISO_Enter || + event->keyval == GDK_KP_Enter || + event->keyval == GDK_Return) { + /* Make sure that user has a selection to choose, otherwise ignore */ + GtkTreeModel * model = gtk_entry_completion_get_model (GTK_ENTRY_COMPLETION (completion)); + GtkTreeView * view = find_popup_treeview (GTK_WIDGET (entry), model); + if (view == NULL) { + // Just beep if popup hasn't appeared yet. + gtk_widget_error_bell (GTK_WIDGET (entry)); + return TRUE; + } + + GtkTreeSelection * sel = gtk_tree_view_get_selection (view); + GtkTreeModel * sel_model = NULL; + if (!gtk_tree_selection_get_selected (sel, &sel_model, NULL)) { + // No selection, we should help them out and select first item in list + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first (sel_model, &iter)) + gtk_tree_selection_select_iter (sel, &iter); + // And fall through to normal handler code + } + } + + return FALSE; +} + void timezone_completion_watch_entry (TimezoneCompletion * completion, GtkEntry * entry) { @@ -341,15 +432,22 @@ timezone_completion_watch_entry (TimezoneCompletion * completion, GtkEntry * ent priv->queued_request = 0; } if (priv->entry) { - g_source_remove (priv->changed_id); + g_signal_handler_disconnect (priv->entry, priv->changed_id); + g_signal_handler_disconnect (priv->entry, priv->keypress_id); g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry); + gtk_entry_set_completion (priv->entry, NULL); } guint id = g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), completion); priv->changed_id = id; + id = g_signal_connect (entry, "key-press-event", G_CALLBACK (entry_keypress), completion); + priv->keypress_id = id; + priv->entry = entry; g_object_add_weak_pointer (G_OBJECT (entry), (gpointer *)&priv->entry); + + gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (completion)); } static GtkListStore * @@ -471,10 +569,17 @@ timezone_completion_dispose (GObject * object) TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion); if (priv->changed_id) { - g_source_remove (priv->changed_id); + if (priv->entry) + g_signal_handler_disconnect (priv->entry, priv->changed_id); priv->changed_id = 0; } + if (priv->keypress_id) { + if (priv->entry) + g_signal_handler_disconnect (priv->entry, priv->keypress_id); + priv->keypress_id = 0; + } + if (priv->entry != NULL) { g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry); } |