[Spice-devel] [spice-gtk PATCH 1/3 v2] spicy-connect: Connect dialog changed to window

Lukas Venhoda lvenhoda at redhat.com
Tue Jun 9 07:59:32 PDT 2015


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



More information about the Spice-devel mailing list