Codebase list virt-viewer / debian/4.0-1 src / virt-viewer-file-transfer-dialog.c
debian/4.0-1

Tree @debian/4.0-1 (Download .tar.gz)

virt-viewer-file-transfer-dialog.c @debian/4.0-1

f00b1ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/*
 * Virt Viewer: A virtual machine console viewer
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <config.h>

#include "virt-viewer-file-transfer-dialog.h"
#include <glib/gi18n.h>

G_DEFINE_TYPE(VirtViewerFileTransferDialog, virt_viewer_file_transfer_dialog, GTK_TYPE_DIALOG)

#define FILE_TRANSFER_DIALOG_PRIVATE(o) \
        (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogPrivate))

struct _VirtViewerFileTransferDialogPrivate
{
    /* GHashTable<SpiceFileTransferTask, widgets> */
    GHashTable *file_transfers;
    guint timer_show_src;
    guint timer_hide_src;
};


static void
virt_viewer_file_transfer_dialog_dispose(GObject *object)
{
    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(object);

    g_clear_pointer(&self->priv->file_transfers, g_hash_table_unref);

    G_OBJECT_CLASS(virt_viewer_file_transfer_dialog_parent_class)->dispose(object);
}

static void
virt_viewer_file_transfer_dialog_class_init(VirtViewerFileTransferDialogClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);

    g_type_class_add_private(klass, sizeof(VirtViewerFileTransferDialogPrivate));

    object_class->dispose = virt_viewer_file_transfer_dialog_dispose;
}

static void
dialog_response(GtkDialog *dialog,
                gint response_id,
                gpointer user_data G_GNUC_UNUSED)
{
    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(dialog);
    GHashTableIter iter;
    gpointer key, value;

    switch (response_id) {
        case GTK_RESPONSE_CANCEL:
            /* cancel all current tasks */
            g_hash_table_iter_init(&iter, self->priv->file_transfers);

            while (g_hash_table_iter_next(&iter, &key, &value)) {
                spice_file_transfer_task_cancel(SPICE_FILE_TRANSFER_TASK(key));
            }
            break;
        case GTK_RESPONSE_DELETE_EVENT:
            /* silently ignore */
            break;
        default:
            g_warn_if_reached();
    }
}

static void task_cancel_clicked(GtkButton *button G_GNUC_UNUSED,
                                gpointer user_data)
{
    SpiceFileTransferTask *task = user_data;
    spice_file_transfer_task_cancel(task);
}

typedef struct {
    GtkWidget *vbox;
    GtkWidget *hbox;
    GtkWidget *progress;
    GtkWidget *label;
    GtkWidget *cancel;
} TaskWidgets;

static TaskWidgets *task_widgets_new(SpiceFileTransferTask *task)
{
    TaskWidgets *w = g_new0(TaskWidgets, 1);

    w->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    w->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
    w->progress = gtk_progress_bar_new();
    w->label = gtk_label_new(spice_file_transfer_task_get_filename(task));
    w->cancel = gtk_button_new_from_icon_name("gtk-cancel", GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_widget_set_hexpand(w->progress, TRUE);
    gtk_widget_set_valign(w->progress, GTK_ALIGN_CENTER);
    gtk_widget_set_hexpand(w->label, TRUE);
    gtk_widget_set_valign(w->label, GTK_ALIGN_END);
    gtk_widget_set_halign(w->label, GTK_ALIGN_START);
    gtk_widget_set_hexpand(w->cancel, FALSE);
    gtk_widget_set_valign(w->cancel, GTK_ALIGN_CENTER);

    g_signal_connect(w->cancel, "clicked",
                     G_CALLBACK(task_cancel_clicked), task);

    gtk_box_pack_start(GTK_BOX(w->hbox), w->progress, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(w->hbox), w->cancel, FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(w->vbox), w->label, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(w->vbox), w->hbox, TRUE, TRUE, 0);

    gtk_widget_show_all(w->vbox);
    return w;
}

static gboolean delete_event(GtkWidget *widget,
                             GdkEvent *event G_GNUC_UNUSED,
                             gpointer user_data G_GNUC_UNUSED)
{
    /* don't allow window to be deleted, just process the response signal,
     * which may result in the window being hidden */
    gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL);
    return TRUE;
}

static void
virt_viewer_file_transfer_dialog_init(VirtViewerFileTransferDialog *self)
{
    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));

    self->priv = FILE_TRANSFER_DIALOG_PRIVATE(self);

    gtk_widget_set_size_request(GTK_WIDGET(content), 400, -1);
    gtk_container_set_border_width(GTK_CONTAINER(content), 12);
    self->priv->file_transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                                       g_object_unref,
                                                       (GDestroyNotify)g_free);
    gtk_dialog_add_button(GTK_DIALOG(self), _("Cancel"), GTK_RESPONSE_CANCEL);
    gtk_dialog_set_default_response(GTK_DIALOG(self),
                                    GTK_RESPONSE_CANCEL);
    g_signal_connect(self, "response", G_CALLBACK(dialog_response), NULL);
    g_signal_connect(self, "delete-event", G_CALLBACK(delete_event), NULL);
}

VirtViewerFileTransferDialog *
virt_viewer_file_transfer_dialog_new(GtkWindow *parent)
{
    return g_object_new(VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG,
                        "title", _("File Transfers"),
                        "transient-for", parent,
                        "resizable", FALSE,
                        NULL);
}

static void task_progress_notify(GObject *object,
                                 GParamSpec *pspec G_GNUC_UNUSED,
                                 gpointer user_data)
{
    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
    SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(object);
    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);
    g_return_if_fail(w);

    double pct = spice_file_transfer_task_get_progress(task);
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(w->progress), pct);
}

static gboolean hide_transfer_dialog(gpointer data)
{
    VirtViewerFileTransferDialog *self = data;
    gtk_widget_hide(GTK_WIDGET(self));
    gtk_dialog_set_response_sensitive(GTK_DIALOG(self),
                                      GTK_RESPONSE_CANCEL, FALSE);
    self->priv->timer_hide_src = 0;

    return G_SOURCE_REMOVE;
}

typedef struct {
    VirtViewerFileTransferDialog *self;
    TaskWidgets *widgets;
    SpiceFileTransferTask *task;
} TaskFinishedData;

static gboolean task_finished_remove(gpointer user_data)
{
    TaskFinishedData *d = user_data;

    gtk_widget_destroy(d->widgets->vbox);

    g_free(d->widgets);
    g_object_unref(d->task);
    g_free(d);

    return G_SOURCE_REMOVE;
}

static void task_finished(SpiceFileTransferTask *task,
                          GError *error,
                          gpointer user_data)
{
    TaskFinishedData *d;
    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);

    if (error && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        g_warning("File transfer task %p failed: %s", task, error->message);

    g_return_if_fail(w);
    gtk_widget_set_sensitive(w->cancel, FALSE);


    d = g_new0(TaskFinishedData, 1);
    d->self = self;
    d->widgets = w;
    d->task = task;

    g_timeout_add(500, task_finished_remove, d);

    g_hash_table_steal(self->priv->file_transfers, task);

    /* if this is the last transfer, close the dialog */
    if (!g_hash_table_size(d->self->priv->file_transfers)) {
        /* cancel any pending 'show' operations if all tasks complete before
         * the dialog can be shown */
        if (self->priv->timer_show_src) {
            g_source_remove(self->priv->timer_show_src);
            self->priv->timer_show_src = 0;
        }
        self->priv->timer_hide_src = g_timeout_add(500, hide_transfer_dialog,
                                                   d->self);
    }
}

static gboolean show_transfer_dialog_delayed(gpointer user_data)
{
    VirtViewerFileTransferDialog *self = user_data;

    self->priv->timer_show_src = 0;
    gtk_widget_show(GTK_WIDGET(self));

    return G_SOURCE_REMOVE;
}

static void show_transfer_dialog(VirtViewerFileTransferDialog *self)
{
    /* if there's a pending 'hide', cancel it */
    if (self->priv->timer_hide_src) {
        g_source_remove(self->priv->timer_hide_src);
        self->priv->timer_hide_src = 0;
    }

    /* don't show the dialog immediately. For very quick transfers, it doesn't
     * make sense to show a dialog and immediately hide it. But if there's
     * already a pending 'show' operation, don't trigger another one */
    if (self->priv->timer_show_src == 0)
        self->priv->timer_show_src = g_timeout_add(250,
                                                   show_transfer_dialog_delayed,
                                                   self);

    gtk_dialog_set_response_sensitive(GTK_DIALOG(self),
                                      GTK_RESPONSE_CANCEL, TRUE);
}

void virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDialog *self,
                                               SpiceFileTransferTask *task)
{
    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));
    TaskWidgets *w = task_widgets_new(task);

    gtk_box_pack_start(content,
                       w->vbox,
                       TRUE, TRUE, 12);
    g_hash_table_insert(self->priv->file_transfers, g_object_ref(task), w);
    g_signal_connect(task, "notify::progress", G_CALLBACK(task_progress_notify), self);
    g_signal_connect(task, "finished", G_CALLBACK(task_finished), self);

    show_transfer_dialog(self);
}