Blame SOURCES/0093-Add-file-transfer-dialog.patch

03a6ff
From d0202bad05d3e50e074fbf6d7fd966aa5de6713c Mon Sep 17 00:00:00 2001
03a6ff
From: Jonathon Jongsma <jjongsma@redhat.com>
03a6ff
Date: Fri, 8 Apr 2016 10:26:32 -0500
03a6ff
Subject: [PATCH] Add file transfer dialog
03a6ff
MIME-Version: 1.0
03a6ff
Content-Type: text/plain; charset=UTF-8
03a6ff
Content-Transfer-Encoding: 8bit
03a6ff
03a6ff
This dialog will show the progress of files being transferred from the
03a6ff
client to the guest and allows the user to cancel ongoing file transfer
03a6ff
tasks.  The user can cancel each transfer individually, or cancel all
03a6ff
ongoing transfers at once.
03a6ff
03a6ff
Resolves: rhbz#1332180, rhbz#1324521
03a6ff
Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
03a6ff
Acked-by: Fabiano FidĂȘncio <fidencio@redhat.com>
03a6ff
(cherry picked from commit 0856067d79db02db86227d2f1beb0b03859613d5)
03a6ff
---
03a6ff
 po/POTFILES.in                         |   1 +
03a6ff
 src/Makefile.am                        |   1 +
03a6ff
 src/virt-viewer-file-transfer-dialog.c | 215 +++++++++++++++++++++++++++++++++
03a6ff
 src/virt-viewer-file-transfer-dialog.h |  61 ++++++++++
03a6ff
 src/virt-viewer-session-spice.c        |  43 +++++--
03a6ff
 5 files changed, 310 insertions(+), 11 deletions(-)
03a6ff
 create mode 100644 src/virt-viewer-file-transfer-dialog.c
03a6ff
 create mode 100644 src/virt-viewer-file-transfer-dialog.h
03a6ff
03a6ff
diff --git a/po/POTFILES.in b/po/POTFILES.in
03a6ff
index f270898..e6cc31f 100644
03a6ff
--- a/po/POTFILES.in
03a6ff
+++ b/po/POTFILES.in
03a6ff
@@ -8,6 +8,7 @@ src/virt-viewer-app.c
03a6ff
 src/virt-viewer-auth.c
03a6ff
 [type: gettext/glade] src/virt-viewer-auth.xml
03a6ff
 src/virt-viewer-display-vnc.c
03a6ff
+src/virt-viewer-file-transfer-dialog.c
03a6ff
 src/virt-viewer-main.c
03a6ff
 src/virt-viewer-session-spice.c
03a6ff
 src/virt-viewer-session-vnc.c
03a6ff
diff --git a/src/Makefile.am b/src/Makefile.am
03a6ff
index b8dbb35..182f58d 100644
03a6ff
--- a/src/Makefile.am
03a6ff
+++ b/src/Makefile.am
03a6ff
@@ -67,6 +67,7 @@ if HAVE_SPICE_GTK
03a6ff
 COMMON_SOURCES +=						\
03a6ff
 	virt-viewer-session-spice.h virt-viewer-session-spice.c	\
03a6ff
 	virt-viewer-display-spice.h virt-viewer-display-spice.c	\
03a6ff
+	virt-viewer-file-transfer-dialog.h virt-viewer-file-transfer-dialog.c \
03a6ff
 	$(NULL)
03a6ff
 endif
03a6ff
 
03a6ff
diff --git a/src/virt-viewer-file-transfer-dialog.c b/src/virt-viewer-file-transfer-dialog.c
03a6ff
new file mode 100644
03a6ff
index 0000000..be16518
03a6ff
--- /dev/null
03a6ff
+++ b/src/virt-viewer-file-transfer-dialog.c
03a6ff
@@ -0,0 +1,215 @@
03a6ff
+/*
03a6ff
+ * Virt Viewer: A virtual machine console viewer
03a6ff
+ *
03a6ff
+ * Copyright (C) 2016 Red Hat, Inc.
03a6ff
+ *
03a6ff
+ * This program is free software; you can redistribute it and/or modify
03a6ff
+ * it under the terms of the GNU General Public License as published by
03a6ff
+ * the Free Software Foundation; either version 2 of the License, or
03a6ff
+ * (at your option) any later version.
03a6ff
+ *
03a6ff
+ * This program is distributed in the hope that it will be useful,
03a6ff
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
03a6ff
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
03a6ff
+ * GNU General Public License for more details.
03a6ff
+ *
03a6ff
+ * You should have received a copy of the GNU General Public License
03a6ff
+ * along with this program; if not, write to the Free Software
03a6ff
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
03a6ff
+ */
03a6ff
+
03a6ff
+#include <config.h>
03a6ff
+
03a6ff
+#include "virt-viewer-file-transfer-dialog.h"
03a6ff
+#include <glib/gi18n.h>
03a6ff
+
03a6ff
+G_DEFINE_TYPE(VirtViewerFileTransferDialog, virt_viewer_file_transfer_dialog, GTK_TYPE_DIALOG)
03a6ff
+
03a6ff
+#define FILE_TRANSFER_DIALOG_PRIVATE(o) \
03a6ff
+        (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogPrivate))
03a6ff
+
03a6ff
+struct _VirtViewerFileTransferDialogPrivate
03a6ff
+{
03a6ff
+    /* GHashTable<SpiceFileTransferTask, widgets> */
03a6ff
+    GHashTable *file_transfers;
03a6ff
+};
03a6ff
+
03a6ff
+
03a6ff
+static void
03a6ff
+virt_viewer_file_transfer_dialog_dispose(GObject *object)
03a6ff
+{
03a6ff
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(object);
03a6ff
+
03a6ff
+    g_clear_pointer(&self->priv->file_transfers, g_hash_table_unref);
03a6ff
+
03a6ff
+    G_OBJECT_CLASS(virt_viewer_file_transfer_dialog_parent_class)->dispose(object);
03a6ff
+}
03a6ff
+
03a6ff
+static void
03a6ff
+virt_viewer_file_transfer_dialog_class_init(VirtViewerFileTransferDialogClass *klass)
03a6ff
+{
03a6ff
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
03a6ff
+
03a6ff
+    g_type_class_add_private(klass, sizeof(VirtViewerFileTransferDialogPrivate));
03a6ff
+
03a6ff
+    object_class->dispose = virt_viewer_file_transfer_dialog_dispose;
03a6ff
+}
03a6ff
+
03a6ff
+static void
03a6ff
+dialog_response(GtkDialog *dialog,
03a6ff
+                gint response_id,
03a6ff
+                gpointer user_data G_GNUC_UNUSED)
03a6ff
+{
03a6ff
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(dialog);
03a6ff
+    GHashTableIter iter;
03a6ff
+    gpointer key, value;
03a6ff
+
03a6ff
+    switch (response_id) {
03a6ff
+        case GTK_RESPONSE_CANCEL:
03a6ff
+            /* cancel all current tasks */
03a6ff
+            g_hash_table_iter_init(&iter, self->priv->file_transfers);
03a6ff
+
03a6ff
+            while (g_hash_table_iter_next(&iter, &key, &value)) {
03a6ff
+                spice_file_transfer_task_cancel(SPICE_FILE_TRANSFER_TASK(key));
03a6ff
+            }
03a6ff
+            break;
03a6ff
+        case GTK_RESPONSE_DELETE_EVENT:
03a6ff
+            /* silently ignore */
03a6ff
+            break;
03a6ff
+        default:
03a6ff
+            g_warn_if_reached();
03a6ff
+    }
03a6ff
+}
03a6ff
+
03a6ff
+static void task_cancel_clicked(GtkButton *button G_GNUC_UNUSED,
03a6ff
+                                gpointer user_data)
03a6ff
+{
03a6ff
+    SpiceFileTransferTask *task = user_data;
03a6ff
+    spice_file_transfer_task_cancel(task);
03a6ff
+}
03a6ff
+
03a6ff
+typedef struct {
03a6ff
+    GtkWidget *vbox;
03a6ff
+    GtkWidget *hbox;
03a6ff
+    GtkWidget *progress;
03a6ff
+    GtkWidget *label;
03a6ff
+    GtkWidget *cancel;
03a6ff
+} TaskWidgets;
03a6ff
+
03a6ff
+static TaskWidgets *task_widgets_new(SpiceFileTransferTask *task)
03a6ff
+{
03a6ff
+    TaskWidgets *w = g_new0(TaskWidgets, 1);
03a6ff
+
03a6ff
+    w->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
03a6ff
+    w->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
03a6ff
+    w->progress = gtk_progress_bar_new();
03a6ff
+    w->label = gtk_label_new(spice_file_transfer_task_get_filename(task));
03a6ff
+    w->cancel = gtk_button_new_from_icon_name("gtk-cancel", GTK_ICON_SIZE_SMALL_TOOLBAR);
03a6ff
+    gtk_widget_set_hexpand(w->progress, TRUE);
03a6ff
+    gtk_widget_set_valign(w->progress, GTK_ALIGN_CENTER);
03a6ff
+    gtk_widget_set_hexpand(w->label, TRUE);
03a6ff
+    gtk_widget_set_valign(w->label, GTK_ALIGN_END);
03a6ff
+    gtk_widget_set_halign(w->label, GTK_ALIGN_START);
03a6ff
+    gtk_widget_set_hexpand(w->cancel, FALSE);
03a6ff
+    gtk_widget_set_valign(w->cancel, GTK_ALIGN_CENTER);
03a6ff
+
03a6ff
+    g_signal_connect(w->cancel, "clicked",
03a6ff
+                     G_CALLBACK(task_cancel_clicked), task);
03a6ff
+
03a6ff
+    gtk_box_pack_start(GTK_BOX(w->hbox), w->progress, TRUE, TRUE, 0);
03a6ff
+    gtk_box_pack_start(GTK_BOX(w->hbox), w->cancel, FALSE, TRUE, 0);
03a6ff
+    gtk_box_pack_start(GTK_BOX(w->vbox), w->label, TRUE, TRUE, 0);
03a6ff
+    gtk_box_pack_start(GTK_BOX(w->vbox), w->hbox, TRUE, TRUE, 0);
03a6ff
+
03a6ff
+    gtk_widget_show_all(w->vbox);
03a6ff
+    return w;
03a6ff
+}
03a6ff
+
03a6ff
+static gboolean delete_event(GtkWidget *widget,
03a6ff
+                             GdkEvent *event G_GNUC_UNUSED,
03a6ff
+                             gpointer user_data G_GNUC_UNUSED)
03a6ff
+{
03a6ff
+    /* don't allow window to be deleted, just process the response signal,
03a6ff
+     * which may result in the window being hidden */
03a6ff
+    gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL);
03a6ff
+    return TRUE;
03a6ff
+}
03a6ff
+
03a6ff
+static void
03a6ff
+virt_viewer_file_transfer_dialog_init(VirtViewerFileTransferDialog *self)
03a6ff
+{
03a6ff
+    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));
03a6ff
+
03a6ff
+    self->priv = FILE_TRANSFER_DIALOG_PRIVATE(self);
03a6ff
+
03a6ff
+    gtk_widget_set_size_request(GTK_WIDGET(content), 400, -1);
03a6ff
+    gtk_container_set_border_width(GTK_CONTAINER(content), 12);
03a6ff
+    self->priv->file_transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
03a6ff
+                                                       g_object_unref,
03a6ff
+                                                       (GDestroyNotify)g_free);
03a6ff
+    gtk_dialog_add_button(GTK_DIALOG(self), _("Cancel"), GTK_RESPONSE_CANCEL);
03a6ff
+    gtk_dialog_set_default_response(GTK_DIALOG(self),
03a6ff
+                                    GTK_RESPONSE_CANCEL);
03a6ff
+    g_signal_connect(self, "response", G_CALLBACK(dialog_response), NULL);
03a6ff
+    g_signal_connect(self, "delete-event", G_CALLBACK(delete_event), NULL);
03a6ff
+}
03a6ff
+
03a6ff
+VirtViewerFileTransferDialog *
03a6ff
+virt_viewer_file_transfer_dialog_new(GtkWindow *parent)
03a6ff
+{
03a6ff
+    return g_object_new(VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG,
03a6ff
+                        "title", _("File Transfers"),
03a6ff
+                        "transient-for", parent,
03a6ff
+                        "resizable", FALSE,
03a6ff
+                        NULL);
03a6ff
+}
03a6ff
+
03a6ff
+static void task_progress_notify(GObject *object,
03a6ff
+                                 GParamSpec *pspec G_GNUC_UNUSED,
03a6ff
+                                 gpointer user_data)
03a6ff
+{
03a6ff
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
03a6ff
+    SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(object);
03a6ff
+    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);
03a6ff
+    g_return_if_fail(w);
03a6ff
+
03a6ff
+    double pct = spice_file_transfer_task_get_progress(task);
03a6ff
+    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(w->progress), pct);
03a6ff
+}
03a6ff
+
03a6ff
+static void task_finished(SpiceFileTransferTask *task,
03a6ff
+                          GError *error,
03a6ff
+                          gpointer user_data)
03a6ff
+{
03a6ff
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
03a6ff
+    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);
03a6ff
+
03a6ff
+    if (error && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
03a6ff
+        g_warning("File transfer task %p failed: %s", task, error->message);
03a6ff
+
03a6ff
+    g_return_if_fail(w);
03a6ff
+
03a6ff
+    gtk_widget_destroy(w->vbox);
03a6ff
+
03a6ff
+    g_hash_table_remove(self->priv->file_transfers, task);
03a6ff
+
03a6ff
+    /* if this is the last transfer, close the dialog */
03a6ff
+    if (!g_hash_table_size(self->priv->file_transfers))
03a6ff
+        gtk_widget_hide(GTK_WIDGET(self));
03a6ff
+}
03a6ff
+
03a6ff
+void virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDialog *self,
03a6ff
+                                               SpiceFileTransferTask *task)
03a6ff
+{
03a6ff
+    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));
03a6ff
+    TaskWidgets *w = task_widgets_new(task);
03a6ff
+
03a6ff
+    gtk_box_pack_start(content,
03a6ff
+                       w->vbox,
03a6ff
+                       TRUE, TRUE, 12);
03a6ff
+    g_hash_table_insert(self->priv->file_transfers, g_object_ref(task), w);
03a6ff
+    g_signal_connect(task, "notify::progress", G_CALLBACK(task_progress_notify), self);
03a6ff
+    g_signal_connect(task, "finished", G_CALLBACK(task_finished), self);
03a6ff
+
03a6ff
+    gtk_widget_show(GTK_WIDGET(self));
03a6ff
+}
03a6ff
diff --git a/src/virt-viewer-file-transfer-dialog.h b/src/virt-viewer-file-transfer-dialog.h
03a6ff
new file mode 100644
03a6ff
index 0000000..8805b14
03a6ff
--- /dev/null
03a6ff
+++ b/src/virt-viewer-file-transfer-dialog.h
03a6ff
@@ -0,0 +1,61 @@
03a6ff
+/*
03a6ff
+ * Virt Viewer: A virtual machine console viewer
03a6ff
+ *
03a6ff
+ * Copyright (C) 2016 Red Hat, Inc.
03a6ff
+ *
03a6ff
+ * This program is free software; you can redistribute it and/or modify
03a6ff
+ * it under the terms of the GNU General Public License as published by
03a6ff
+ * the Free Software Foundation; either version 2 of the License, or
03a6ff
+ * (at your option) any later version.
03a6ff
+ *
03a6ff
+ * This program is distributed in the hope that it will be useful,
03a6ff
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
03a6ff
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
03a6ff
+ * GNU General Public License for more details.
03a6ff
+ *
03a6ff
+ * You should have received a copy of the GNU General Public License
03a6ff
+ * along with this program; if not, write to the Free Software
03a6ff
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
03a6ff
+ */
03a6ff
+
03a6ff
+#ifndef __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__
03a6ff
+#define __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__
03a6ff
+
03a6ff
+#include <gtk/gtk.h>
03a6ff
+#include <spice-client.h>
03a6ff
+
03a6ff
+G_BEGIN_DECLS
03a6ff
+
03a6ff
+#define VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG virt_viewer_file_transfer_dialog_get_type()
03a6ff
+
03a6ff
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialog))
03a6ff
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogClass))
03a6ff
+#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG))
03a6ff
+#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG))
03a6ff
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogClass))
03a6ff
+
03a6ff
+typedef struct _VirtViewerFileTransferDialog VirtViewerFileTransferDialog;
03a6ff
+typedef struct _VirtViewerFileTransferDialogClass VirtViewerFileTransferDialogClass;
03a6ff
+typedef struct _VirtViewerFileTransferDialogPrivate VirtViewerFileTransferDialogPrivate;
03a6ff
+
03a6ff
+struct _VirtViewerFileTransferDialog
03a6ff
+{
03a6ff
+    GtkDialog parent;
03a6ff
+
03a6ff
+    VirtViewerFileTransferDialogPrivate *priv;
03a6ff
+};
03a6ff
+
03a6ff
+struct _VirtViewerFileTransferDialogClass
03a6ff
+{
03a6ff
+    GtkDialogClass parent_class;
03a6ff
+};
03a6ff
+
03a6ff
+GType virt_viewer_file_transfer_dialog_get_type(void) G_GNUC_CONST;
03a6ff
+
03a6ff
+VirtViewerFileTransferDialog *virt_viewer_file_transfer_dialog_new(GtkWindow *parent);
03a6ff
+void virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDialog *self,
03a6ff
+                                               SpiceFileTransferTask *task);
03a6ff
+
03a6ff
+G_END_DECLS
03a6ff
+
03a6ff
+#endif /* __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__ */
03a6ff
diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c
03a6ff
index 1f5242c..326336f 100644
03a6ff
--- a/src/virt-viewer-session-spice.c
03a6ff
+++ b/src/virt-viewer-session-spice.c
03a6ff
@@ -33,21 +33,13 @@
03a6ff
 
03a6ff
 #include <usb-device-widget.h>
03a6ff
 #include "virt-viewer-file.h"
03a6ff
+#include "virt-viewer-file-transfer-dialog.h"
03a6ff
 #include "virt-viewer-util.h"
03a6ff
 #include "virt-viewer-session-spice.h"
03a6ff
 #include "virt-viewer-display-spice.h"
03a6ff
 #include "virt-viewer-auth.h"
03a6ff
 #include "virt-glib-compat.h"
03a6ff
 
03a6ff
-#if !GLIB_CHECK_VERSION(2, 26, 0)
03a6ff
-#include "gbinding.h"
03a6ff
-#include "gbinding.c"
03a6ff
-#endif
03a6ff
-
03a6ff
-#ifndef SPICE_GTK_CHECK_VERSION
03a6ff
-#define SPICE_GTK_CHECK_VERSION(x, y, z) 0
03a6ff
-#endif
03a6ff
-
03a6ff
 G_DEFINE_TYPE (VirtViewerSessionSpice, virt_viewer_session_spice, VIRT_VIEWER_TYPE_SESSION)
03a6ff
 
03a6ff
 
03a6ff
@@ -62,6 +54,8 @@ struct _VirtViewerSessionSpicePrivate {
03a6ff
     gboolean has_sw_smartcard_reader;
03a6ff
     guint pass_try;
03a6ff
     gboolean did_auto_conf;
03a6ff
+    VirtViewerFileTransferDialog *file_transfer_dialog;
03a6ff
+
03a6ff
 };
03a6ff
 
03a6ff
 #define VIRT_VIEWER_SESSION_SPICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_SESSION_SPICE, VirtViewerSessionSpicePrivate))
03a6ff
@@ -132,8 +126,11 @@ virt_viewer_session_spice_dispose(GObject *obj)
03a6ff
 
03a6ff
     spice->priv->audio = NULL;
03a6ff
 
03a6ff
-    if (spice->priv->main_window)
03a6ff
-        g_object_unref(spice->priv->main_window);
03a6ff
+    g_clear_object(&spice->priv->main_window);
03a6ff
+    if (spice->priv->file_transfer_dialog) {
03a6ff
+        gtk_widget_destroy(GTK_WIDGET(spice->priv->file_transfer_dialog));
03a6ff
+        spice->priv->file_transfer_dialog = NULL;
03a6ff
+    }
03a6ff
 
03a6ff
     G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->dispose(obj);
03a6ff
 }
03a6ff
@@ -146,6 +143,17 @@ virt_viewer_session_spice_mime_type(VirtViewerSession *self G_GNUC_UNUSED)
03a6ff
 }
03a6ff
 
03a6ff
 static void
03a6ff
+virt_viewer_session_spice_constructed(GObject *obj)
03a6ff
+{
03a6ff
+    VirtViewerSessionSpice *self = VIRT_VIEWER_SESSION_SPICE(obj);
03a6ff
+
03a6ff
+    self->priv->file_transfer_dialog =
03a6ff
+        virt_viewer_file_transfer_dialog_new(self->priv->main_window);
03a6ff
+
03a6ff
+    G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->constructed(obj);
03a6ff
+}
03a6ff
+
03a6ff
+static void
03a6ff
 virt_viewer_session_spice_class_init(VirtViewerSessionSpiceClass *klass)
03a6ff
 {
03a6ff
     VirtViewerSessionClass *dclass = VIRT_VIEWER_SESSION_CLASS(klass);
03a6ff
@@ -154,6 +162,7 @@ virt_viewer_session_spice_class_init(VirtViewerSessionSpiceClass *klass)
03a6ff
     oclass->get_property = virt_viewer_session_spice_get_property;
03a6ff
     oclass->set_property = virt_viewer_session_spice_set_property;
03a6ff
     oclass->dispose = virt_viewer_session_spice_dispose;
03a6ff
+    oclass->constructed = virt_viewer_session_spice_constructed;
03a6ff
 
03a6ff
     dclass->close = virt_viewer_session_spice_close;
03a6ff
     dclass->open_fd = virt_viewer_session_spice_open_fd;
03a6ff
@@ -777,6 +786,16 @@ virt_viewer_session_spice_display_monitors(SpiceChannel *channel,
03a6ff
 }
03a6ff
 
03a6ff
 static void
03a6ff
+on_new_file_transfer(SpiceMainChannel *channel G_GNUC_UNUSED,
03a6ff
+                     SpiceFileTransferTask *task,
03a6ff
+                     gpointer user_data)
03a6ff
+{
03a6ff
+    VirtViewerSessionSpice *self = VIRT_VIEWER_SESSION_SPICE(user_data);
03a6ff
+    virt_viewer_file_transfer_dialog_add_task(self->priv->file_transfer_dialog,
03a6ff
+                                              task);
03a6ff
+}
03a6ff
+
03a6ff
+static void
03a6ff
 virt_viewer_session_spice_channel_new(SpiceSession *s,
03a6ff
                                       SpiceChannel *channel,
03a6ff
                                       VirtViewerSession *session)
03a6ff
@@ -808,6 +827,8 @@ virt_viewer_session_spice_channel_new(SpiceSession *s,
03a6ff
 
03a6ff
         virt_viewer_signal_connect_object(channel, "notify::agent-connected",
03a6ff
                                           G_CALLBACK(agent_connected_changed), self, 0);
03a6ff
+        virt_viewer_signal_connect_object(channel, "new-file-transfer",
03a6ff
+                                          G_CALLBACK(on_new_file_transfer), self, 0);
03a6ff
     }
03a6ff
 
03a6ff
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {