Connect dialog in spicy had no transinient parent. It didn't make much sense for it to be a dialog. Changed the connect dialog to a window. Moved the dialog code to its own module. Changed response ID from ambiguous -1 and 0 to GTK_RESPONSE_ACCEPT and GTK_RESPONSE_REJECT. Improved UX: - ESC to close, ENTER to connect. - Address and port are now required to connect. - Focusing in entries will now unselect recent connection. - If you rewrite something in entries, you can now reselect connection in the recent chooser. --- Changes since v1: - Rebased for latest directory structure - Moved UI back from XML to code - Building from XML would require large rewrite of Makefile - Rest of SPICY is written in code, so making XML for 1 dialog is weird - Changed response IDs from magical numbers to GTK enums - Improved UX --- src/spicy-connect.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/spicy-connect.h | 26 +++++ 2 files changed, 319 insertions(+) create mode 100644 src/spicy-connect.c create mode 100644 src/spicy-connect.h diff --git a/src/spicy-connect.c b/src/spicy-connect.c new file mode 100644 index 0000000..3024b84 --- /dev/null +++ b/src/spicy-connect.c @@ -0,0 +1,293 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2010-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include "spice-common.h" +#include "spicy-connect.h" + +typedef struct +{ + GtkResponseType response_id; + GMainLoop *loop; + SpiceSession *session; +} ConnectionInfo; + +static struct { + const char *text; + const char *prop; + GtkWidget *entry; +} connect_entries[] = { + { .text = N_("Hostname"), .prop = "host" }, + { .text = N_("Port"), .prop = "port" }, + { .text = N_("TLS Port"), .prop = "tls-port" }, +}; + +static void +shutdown_loop(GMainLoop *loop) +{ + if (g_main_loop_is_running(loop)) + g_main_loop_quit(loop); +} + +static void +set_connection_info(SpiceSession *session) +{ + const gchar *txt; + txt = gtk_entry_get_text(GTK_ENTRY(connect_entries[0].entry)); + g_object_set(session, "host", txt, NULL); + + txt = gtk_entry_get_text(GTK_ENTRY(connect_entries[1].entry)); + g_object_set(session, "port", txt, NULL); + + txt = gtk_entry_get_text(GTK_ENTRY(connect_entries[2].entry)); + g_object_set(session, "tls-port", txt, NULL); +} + +static gboolean +can_connect(void) +{ + if ((gtk_entry_get_text_length(GTK_ENTRY(connect_entries[0].entry)) > 0) && + (gtk_entry_get_text_length(GTK_ENTRY(connect_entries[1].entry)) > 0)) + return TRUE; + + return FALSE; +} + +static gboolean +window_deleted_cb(GtkWindow *window, GdkEventAny *event, gpointer data) +{ + ConnectionInfo *ci = data; + ci->response_id = GTK_RESPONSE_REJECT; + shutdown_loop(ci->loop); + return TRUE; +} + +static void +cancel_button_clicked_cb(GtkButton *button, gpointer data) +{ + ConnectionInfo *ci = data; + ci->response_id = GTK_RESPONSE_REJECT; + shutdown_loop(ci->loop); +} + +static void +connect_button_clicked_cb(GtkButton *button, gpointer data) +{ + ConnectionInfo *ci = data; + if (can_connect()) + { + ci->response_id = GTK_RESPONSE_ACCEPT; + set_connection_info(ci->session); + shutdown_loop(ci->loop); + } +} + +static void +entry_activated_cb(GtkEntry *entry, gpointer data) +{ + ConnectionInfo *ci = data; + + if (can_connect()) + { + ci->response_id = GTK_RESPONSE_ACCEPT; + set_connection_info(ci->session); + shutdown_loop(ci->loop); + } +} + +static gboolean +entry_focus_in_cb(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + GtkRecentChooser *recent = data; + gtk_recent_chooser_unselect_all(recent); + return TRUE; +} + +static void +recent_item_activated_dialog_cb(GtkRecentChooser *chooser, gpointer data) +{ + ConnectionInfo *ci = data; + + if (can_connect()) + { + ci->response_id = GTK_RESPONSE_ACCEPT; + set_connection_info(ci->session); + shutdown_loop(ci->loop); + } +} + +static void +recent_selection_changed_dialog_cb(GtkRecentChooser *chooser, gpointer data) +{ + GtkRecentInfo *info; + gchar *txt = NULL; + const gchar *uri; + SpiceSession *session = data; + + info = gtk_recent_chooser_get_current_item(chooser); + if (info == NULL) + return; + + uri = gtk_recent_info_get_uri(info); + g_return_if_fail(uri != NULL); + + g_object_set(session, "uri", uri, NULL); + + g_object_get(session, "host", &txt, NULL); + gtk_entry_set_text(GTK_ENTRY(connect_entries[0].entry), txt ? txt : ""); + g_free(txt); + + g_object_get(session, "port", &txt, NULL); + gtk_entry_set_text(GTK_ENTRY(connect_entries[1].entry), txt ? txt : ""); + g_free(txt); + + g_object_get(session, "tls-port", &txt, NULL); + gtk_entry_set_text(GTK_ENTRY(connect_entries[2].entry), txt ? txt : ""); + g_free(txt); + + gtk_recent_info_unref(info); +} + +static gboolean +key_pressed_cb(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + gboolean tst; + if (event->type == GDK_KEY_PRESS) { + switch (event->key.keyval) { + case GDK_KEY_Escape: + g_signal_emit_by_name(GTK_WIDGET(data), "delete-event", NULL, &tst); + return TRUE; + default: + return FALSE; + } + } + + return FALSE; +} + +int +connect_dialog(SpiceSession *session) +{ + GtkWidget *connect_button, *cancel_button, *label; + GtkBox *main_box, *recent_box, *button_box; + GtkRecentChooser *recent; + GtkRecentFilter *rfilter; + GtkWindow *window; + GtkTable *table; + int i; + + ConnectionInfo ci = { + GTK_RESPONSE_NONE, + NULL, + session, + NULL, + NULL, + NULL + }; + + window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + gtk_window_set_title(window, _("Connect to SPICE")); + gtk_window_set_resizable(window, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + + main_box = GTK_BOX(gtk_vbox_new(FALSE, 0)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(main_box)); + + table = GTK_TABLE(gtk_table_new(3, 2, 0)); + gtk_box_pack_start(main_box, GTK_WIDGET(table), FALSE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + gtk_table_set_row_spacings(table, 5); + gtk_table_set_col_spacings(table, 5); + + for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) { + gchar *txt; + + label = gtk_label_new(connect_entries[i].text); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(table, label, 0, 1, i, i+1); + + connect_entries[i].entry = GTK_WIDGET(gtk_entry_new()); + gtk_table_attach_defaults(table, connect_entries[i].entry, 1, 2, i, i+1); + + g_object_get(session, connect_entries[i].prop, &txt, NULL); + if (txt) { + gtk_entry_set_text(GTK_ENTRY(connect_entries[i].entry), txt); + g_free(txt); + } + } + + recent_box = GTK_BOX(gtk_vbox_new(FALSE, 0)); + gtk_box_pack_start(main_box, GTK_WIDGET(recent_box), TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(recent_box), 5); + + label = gtk_label_new("Recent connections:"); + gtk_box_pack_start(recent_box, label, FALSE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + recent = GTK_RECENT_CHOOSER(gtk_recent_chooser_widget_new()); + gtk_recent_chooser_set_show_icons(recent, FALSE); + gtk_box_pack_start(recent_box, GTK_WIDGET(recent), TRUE, TRUE, 0); + + rfilter = gtk_recent_filter_new(); + gtk_recent_filter_add_mime_type(rfilter, "application/x-spice"); + gtk_recent_chooser_set_filter(recent, rfilter); + gtk_recent_chooser_set_local_only(recent, FALSE); + + button_box = GTK_BOX(gtk_hbutton_box_new()); + gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END); + gtk_box_set_spacing(button_box, 5); + gtk_container_set_border_width(GTK_CONTAINER(button_box), 5); + connect_button = gtk_button_new_with_label("Connect"); + cancel_button = gtk_button_new_with_label("Cancel"); + gtk_box_pack_start(button_box, cancel_button, FALSE, TRUE, 0); + gtk_box_pack_start(button_box, connect_button, FALSE, TRUE, 1); + + gtk_box_pack_start(main_box, GTK_WIDGET(button_box), FALSE, TRUE, 0); + + g_signal_connect(window, "delete-event", + G_CALLBACK(window_deleted_cb), &ci); + g_signal_connect(window, "key-press-event", + G_CALLBACK(key_pressed_cb), window); + g_signal_connect(connect_button, "clicked", + G_CALLBACK(connect_button_clicked_cb), &ci); + g_signal_connect(cancel_button, "clicked", + G_CALLBACK(cancel_button_clicked_cb), &ci); + + for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) { + g_signal_connect(connect_entries[i].entry, "activate", + G_CALLBACK(entry_activated_cb), &ci); + g_signal_connect(connect_entries[i].entry, "focus-in-event", + G_CALLBACK(entry_focus_in_cb), recent); + } + + g_signal_connect(recent, "selection-changed", + G_CALLBACK(recent_selection_changed_dialog_cb), session); + g_signal_connect(recent, "item-activated", + G_CALLBACK(recent_item_activated_dialog_cb), &ci); + + gtk_widget_show_all(GTK_WIDGET(window)); + + ci.loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(ci.loop); + + gtk_widget_destroy(GTK_WIDGET(window)); + + return ci.response_id; +} diff --git a/src/spicy-connect.h b/src/spicy-connect.h new file mode 100644 index 0000000..3f1869d --- /dev/null +++ b/src/spicy-connect.h @@ -0,0 +1,26 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2010-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef SPICY_CONNECT_H +#define SPICY_CONNECT_H + +#include "spice-widget.h" + +int connect_dialog(SpiceSession *session); + +#endif -- 2.4.2 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel