Blame SOURCES/0009-Introduce-ISO-List-dialog.patch

1efd99
From c2529913c72a00c5228d02a8e18a3991ac8ba00a Mon Sep 17 00:00:00 2001
1efd99
From: "Eduardo Lima (Etrunko)" <etrunko@redhat.com>
1efd99
Date: Fri, 8 Jul 2016 10:30:51 -0300
1efd99
Subject: [PATCH 09/26] Introduce ISO List dialog
1efd99
1efd99
The motivation for this dialog started with rhbz #1310624, where it was
1efd99
reported that foreign menu was causing too many debug messages to be
1efd99
printed to the console, because remote viewer had a timeout of 5 seconds
1efd99
to refresh the ISO list automatically.
1efd99
1efd99
As a workaround, the timeout was adjusted for 5 minutes, but it could
1efd99
cause more problems, such as inconsistencies between what was shown by
1efd99
remote viewer and what the server had configured.
1efd99
1efd99
Another issue caused by displaying the ISO files as a menu item was that
1efd99
if the list was too long, it would take all the available space on the
1efd99
screen. In the end, a menu item was not the correct choice of UI
1efd99
component for this use case.
1efd99
1efd99
In order to solve both problems, we now present the ISO list as a
1efd99
dedicated dialog, where the refresh of ISO list is triggered manually by
1efd99
the user and the list is contained within the dialog, by displaying de
1efd99
files in a treeview.
1efd99
1efd99
Signed-off-by: Eduardo Lima (Etrunko) <etrunko@redhat.com>
1efd99
---
1efd99
 po/POTFILES.in                             |   2 +
1efd99
 src/Makefile.am                            |   3 +
1efd99
 src/remote-viewer-iso-list-dialog.c        | 365 +++++++++++++++++++++++++++++
1efd99
 src/remote-viewer-iso-list-dialog.h        |  58 +++++
1efd99
 src/resources/ui/remote-viewer-iso-list.ui | 158 +++++++++++++
1efd99
 src/resources/virt-viewer.gresource.xml    |   1 +
1efd99
 6 files changed, 587 insertions(+)
1efd99
 create mode 100644 src/remote-viewer-iso-list-dialog.c
1efd99
 create mode 100644 src/remote-viewer-iso-list-dialog.h
1efd99
 create mode 100644 src/resources/ui/remote-viewer-iso-list.ui
1efd99
1efd99
diff --git a/po/POTFILES.in b/po/POTFILES.in
1efd99
index 69d9fef..371c242 100644
1efd99
--- a/po/POTFILES.in
1efd99
+++ b/po/POTFILES.in
1efd99
@@ -1,9 +1,11 @@
1efd99
 data/remote-viewer.appdata.xml.in
1efd99
 data/remote-viewer.desktop.in
1efd99
 data/virt-viewer-mime.xml.in
1efd99
+src/remote-viewer-iso-list-dialog.c
1efd99
 src/remote-viewer-main.c
1efd99
 src/remote-viewer.c
1efd99
 [type: gettext/glade] src/resources/ui/remote-viewer-connect.ui
1efd99
+[type: gettext/glade] src/resources/ui/remote-viewer-iso-list.ui
1efd99
 [type: gettext/glade] src/resources/ui/virt-viewer-about.ui
1efd99
 src/virt-viewer-app.c
1efd99
 src/virt-viewer-auth.c
1efd99
diff --git a/src/Makefile.am b/src/Makefile.am
1efd99
index 272c4ff..9748277 100644
1efd99
--- a/src/Makefile.am
1efd99
+++ b/src/Makefile.am
1efd99
@@ -13,6 +13,7 @@ noinst_DATA = \
1efd99
 	resources/ui/virt-viewer-vm-connection.ui \
1efd99
 	resources/ui/virt-viewer-preferences.ui \
1efd99
 	resources/ui/remote-viewer-connect.ui \
1efd99
+	resources/ui/remote-viewer-iso-list.ui \
1efd99
 	resources/ui/virt-viewer-file-transfer-dialog.ui \
1efd99
 	$(NULL)
1efd99
 
1efd99
@@ -97,6 +98,8 @@ if HAVE_OVIRT
1efd99
 libvirt_viewer_la_SOURCES += \
1efd99
 	ovirt-foreign-menu.h \
1efd99
 	ovirt-foreign-menu.c \
1efd99
+	remote-viewer-iso-list-dialog.c \
1efd99
+	remote-viewer-iso-list-dialog.h \
1efd99
 	$(NULL)
1efd99
 endif
1efd99
 
1efd99
diff --git a/src/remote-viewer-iso-list-dialog.c b/src/remote-viewer-iso-list-dialog.c
1efd99
new file mode 100644
1efd99
index 0000000..f23ddb2
1efd99
--- /dev/null
1efd99
+++ b/src/remote-viewer-iso-list-dialog.c
1efd99
@@ -0,0 +1,365 @@
1efd99
+/*
1efd99
+ * Virt Viewer: A virtual machine console viewer
1efd99
+ *
1efd99
+ * Copyright (C) 2017 Red Hat, Inc.
1efd99
+ *
1efd99
+ * This program is free software; you can redistribute it and/or modify
1efd99
+ * it under the terms of the GNU General Public License as published by
1efd99
+ * the Free Software Foundation; either version 2 of the License, or
1efd99
+ * (at your option) any later version.
1efd99
+ *
1efd99
+ * This program is distributed in the hope that it will be useful,
1efd99
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1efd99
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1efd99
+ * GNU General Public License for more details.
1efd99
+ *
1efd99
+ * You should have received a copy of the GNU General Public License
1efd99
+ * along with this program; if not, write to the Free Software
1efd99
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1efd99
+ */
1efd99
+
1efd99
+#include <config.h>
1efd99
+
1efd99
+#include <glib/gi18n.h>
1efd99
+
1efd99
+#include "remote-viewer-iso-list-dialog.h"
1efd99
+#include "virt-viewer-util.h"
1efd99
+#include "ovirt-foreign-menu.h"
1efd99
+
1efd99
+static void ovirt_foreign_menu_iso_name_changed(OvirtForeignMenu *foreign_menu, GAsyncResult *result, RemoteViewerISOListDialog *self);
1efd99
+static void remote_viewer_iso_list_dialog_show_error(RemoteViewerISOListDialog *self, const gchar *message);
1efd99
+
1efd99
+G_DEFINE_TYPE(RemoteViewerISOListDialog, remote_viewer_iso_list_dialog, GTK_TYPE_DIALOG)
1efd99
+
1efd99
+#define DIALOG_PRIVATE(o) \
1efd99
+        (G_TYPE_INSTANCE_GET_PRIVATE((o), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG, RemoteViewerISOListDialogPrivate))
1efd99
+
1efd99
+struct _RemoteViewerISOListDialogPrivate
1efd99
+{
1efd99
+    GtkListStore *list_store;
1efd99
+    GtkWidget *status;
1efd99
+    GtkWidget *spinner;
1efd99
+    GtkWidget *stack;
1efd99
+    GtkWidget *tree_view;
1efd99
+    OvirtForeignMenu *foreign_menu;
1efd99
+};
1efd99
+
1efd99
+enum RemoteViewerISOListDialogModel
1efd99
+{
1efd99
+    ISO_IS_ACTIVE = 0,
1efd99
+    ISO_NAME,
1efd99
+    FONT_WEIGHT,
1efd99
+};
1efd99
+
1efd99
+enum RemoteViewerISOListDialogProperties {
1efd99
+    PROP_0,
1efd99
+    PROP_FOREIGN_MENU,
1efd99
+};
1efd99
+
1efd99
+
1efd99
+void remote_viewer_iso_list_dialog_toggled(GtkCellRendererToggle *cell_renderer, gchar *path, gpointer user_data);
1efd99
+void remote_viewer_iso_list_dialog_row_activated(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data);
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_dispose(GObject *object)
1efd99
+{
1efd99
+    RemoteViewerISOListDialog *self = REMOTE_VIEWER_ISO_LIST_DIALOG(object);
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+
1efd99
+    if (priv->foreign_menu) {
1efd99
+        g_signal_handlers_disconnect_by_data(priv->foreign_menu, object);
1efd99
+        g_clear_object(&priv->foreign_menu);
1efd99
+    }
1efd99
+    G_OBJECT_CLASS(remote_viewer_iso_list_dialog_parent_class)->dispose(object);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_set_property(GObject *object, guint property_id,
1efd99
+                                           const GValue *value, GParamSpec *pspec)
1efd99
+{
1efd99
+    RemoteViewerISOListDialog *self = REMOTE_VIEWER_ISO_LIST_DIALOG(object);
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+
1efd99
+    switch (property_id) {
1efd99
+    case PROP_FOREIGN_MENU:
1efd99
+        priv->foreign_menu = g_value_dup_object(value);
1efd99
+        break;
1efd99
+    default:
1efd99
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1efd99
+    }
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_class_init(RemoteViewerISOListDialogClass *klass)
1efd99
+{
1efd99
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
1efd99
+
1efd99
+    g_type_class_add_private(klass, sizeof(RemoteViewerISOListDialogPrivate));
1efd99
+
1efd99
+    object_class->dispose = remote_viewer_iso_list_dialog_dispose;
1efd99
+    object_class->set_property = remote_viewer_iso_list_dialog_set_property;
1efd99
+
1efd99
+    g_object_class_install_property(object_class,
1efd99
+                                    PROP_FOREIGN_MENU,
1efd99
+                                    g_param_spec_object("foreign-menu",
1efd99
+                                                        "oVirt Foreign Menu",
1efd99
+                                                        "Object which is used as interface to oVirt",
1efd99
+                                                        OVIRT_TYPE_FOREIGN_MENU,
1efd99
+                                                        G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_show_files(RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    self->priv = DIALOG_PRIVATE(self);
1efd99
+    gtk_stack_set_visible_child_full(GTK_STACK(self->priv->stack), "iso-list",
1efd99
+                                     GTK_STACK_TRANSITION_TYPE_NONE);
1efd99
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, TRUE);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_foreach(char *name, RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+    gchar *current_iso = ovirt_foreign_menu_get_current_iso_name(self->priv->foreign_menu);
1efd99
+    gboolean active = (g_strcmp0(current_iso, name) == 0);
1efd99
+    gint weight = active ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
1efd99
+    GtkTreeIter iter;
1efd99
+
1efd99
+    gtk_list_store_append(priv->list_store, &iter);
1efd99
+    gtk_list_store_set(priv->list_store, &iter,
1efd99
+                       ISO_IS_ACTIVE, active,
1efd99
+                       ISO_NAME, name,
1efd99
+                       FONT_WEIGHT, weight, -1);
1efd99
+
1efd99
+    if (active) {
1efd99
+        GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->list_store), &iter);
1efd99
+        gtk_tree_view_set_cursor(GTK_TREE_VIEW(priv->tree_view), path, NULL, FALSE);
1efd99
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->tree_view), path, NULL, TRUE, 0.5, 0.5);
1efd99
+        gtk_tree_path_free(path);
1efd99
+    }
1efd99
+
1efd99
+    g_free(current_iso);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+fetch_iso_names_cb(OvirtForeignMenu *foreign_menu,
1efd99
+                   GAsyncResult *result,
1efd99
+                   RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+    GError *error = NULL;
1efd99
+    GList *iso_list;
1efd99
+
1efd99
+    iso_list = ovirt_foreign_menu_fetch_iso_names_finish(foreign_menu, result, &error);
1efd99
+
1efd99
+    if (!iso_list) {
1efd99
+        const gchar *msg = error ? error->message : _("Failed to fetch CD names");
1efd99
+        gchar *markup = g_strdup_printf("%s", msg);
1efd99
+
1efd99
+        gtk_label_set_markup(GTK_LABEL(priv->status), markup);
1efd99
+        gtk_spinner_stop(GTK_SPINNER(priv->spinner));
1efd99
+        remote_viewer_iso_list_dialog_show_error(self, msg);
1efd99
+        gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, TRUE);
1efd99
+        g_free(markup);
1efd99
+        g_clear_error(&error);
1efd99
+        return;
1efd99
+    }
1efd99
+
1efd99
+    g_list_foreach(iso_list, (GFunc) remote_viewer_iso_list_dialog_foreach, self);
1efd99
+    remote_viewer_iso_list_dialog_show_files(self);
1efd99
+}
1efd99
+
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_refresh_iso_list(RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+
1efd99
+    gtk_list_store_clear(priv->list_store);
1efd99
+    ovirt_foreign_menu_fetch_iso_names_async(priv->foreign_menu, NULL,
1efd99
+                                             (GAsyncReadyCallback) fetch_iso_names_cb,
1efd99
+                                             self);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_response(GtkDialog *dialog,
1efd99
+                                       gint response_id,
1efd99
+                                       gpointer user_data G_GNUC_UNUSED)
1efd99
+{
1efd99
+    RemoteViewerISOListDialog *self = REMOTE_VIEWER_ISO_LIST_DIALOG(dialog);
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+
1efd99
+    if (response_id != GTK_RESPONSE_NONE)
1efd99
+        return;
1efd99
+
1efd99
+    gtk_spinner_start(GTK_SPINNER(priv->spinner));
1efd99
+    gtk_label_set_markup(GTK_LABEL(priv->status), _("Loading..."));
1efd99
+    gtk_stack_set_visible_child_full(GTK_STACK(priv->stack), "status",
1efd99
+                                     GTK_STACK_TRANSITION_TYPE_NONE);
1efd99
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, FALSE);
1efd99
+    remote_viewer_iso_list_dialog_refresh_iso_list(self);
1efd99
+}
1efd99
+
1efd99
+void
1efd99
+remote_viewer_iso_list_dialog_toggled(GtkCellRendererToggle *cell_renderer G_GNUC_UNUSED,
1efd99
+                                      gchar *path,
1efd99
+                                      gpointer user_data)
1efd99
+{
1efd99
+    RemoteViewerISOListDialog *self = REMOTE_VIEWER_ISO_LIST_DIALOG(user_data);
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+    GtkTreeModel *model = GTK_TREE_MODEL(priv->list_store);
1efd99
+    GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
1efd99
+    GtkTreeIter iter;
1efd99
+    gboolean active;
1efd99
+    gchar *name;
1efd99
+
1efd99
+    gtk_tree_view_set_cursor(GTK_TREE_VIEW(priv->tree_view), tree_path, NULL, FALSE);
1efd99
+    gtk_tree_model_get_iter(model, &iter, tree_path);
1efd99
+    gtk_tree_model_get(model, &iter,
1efd99
+                       ISO_IS_ACTIVE, &active,
1efd99
+                       ISO_NAME, &name, -1);
1efd99
+
1efd99
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, FALSE);
1efd99
+    gtk_widget_set_sensitive(priv->tree_view, FALSE);
1efd99
+
1efd99
+    ovirt_foreign_menu_set_current_iso_name_async(priv->foreign_menu, active ? NULL : name, NULL,
1efd99
+                                                  (GAsyncReadyCallback)ovirt_foreign_menu_iso_name_changed,
1efd99
+                                                  self);
1efd99
+    gtk_tree_path_free(tree_path);
1efd99
+    g_free(name);
1efd99
+}
1efd99
+
1efd99
+void
1efd99
+remote_viewer_iso_list_dialog_row_activated(GtkTreeView *view G_GNUC_UNUSED,
1efd99
+                                            GtkTreePath *path,
1efd99
+                                            GtkTreeViewColumn *col G_GNUC_UNUSED,
1efd99
+                                            gpointer user_data)
1efd99
+{
1efd99
+    gchar *path_str = gtk_tree_path_to_string(path);
1efd99
+    remote_viewer_iso_list_dialog_toggled(NULL, path_str, user_data);
1efd99
+    g_free(path_str);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_init(RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(self));
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv = DIALOG_PRIVATE(self);
1efd99
+    GtkBuilder *builder = virt_viewer_util_load_ui("remote-viewer-iso-list.ui");
1efd99
+    GtkCellRendererToggle *cell_renderer;
1efd99
+
1efd99
+    gtk_builder_connect_signals(builder, self);
1efd99
+
1efd99
+    priv->status = GTK_WIDGET(gtk_builder_get_object(builder, "status"));
1efd99
+    priv->spinner = GTK_WIDGET(gtk_builder_get_object(builder, "spinner"));
1efd99
+    priv->stack = GTK_WIDGET(gtk_builder_get_object(builder, "stack"));
1efd99
+    gtk_box_pack_start(GTK_BOX(content), priv->stack, TRUE, TRUE, 0);
1efd99
+
1efd99
+    priv->list_store = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore"));
1efd99
+    priv->tree_view = GTK_WIDGET(gtk_builder_get_object(builder, "view"));
1efd99
+    cell_renderer = GTK_CELL_RENDERER_TOGGLE(gtk_builder_get_object(builder, "cellrenderertoggle"));
1efd99
+    gtk_cell_renderer_toggle_set_radio(cell_renderer, TRUE);
1efd99
+    gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(cell_renderer), 6, 6);
1efd99
+
1efd99
+    g_object_unref(builder);
1efd99
+
1efd99
+    gtk_dialog_add_buttons(GTK_DIALOG(self),
1efd99
+                           _("Refresh"), GTK_RESPONSE_NONE,
1efd99
+                           _("Close"), GTK_RESPONSE_CLOSE,
1efd99
+                           NULL);
1efd99
+
1efd99
+    gtk_dialog_set_default_response(GTK_DIALOG(self), GTK_RESPONSE_CLOSE);
1efd99
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, FALSE);
1efd99
+    g_signal_connect(self, "response", G_CALLBACK(remote_viewer_iso_list_dialog_response), NULL);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+remote_viewer_iso_list_dialog_show_error(RemoteViewerISOListDialog *self,
1efd99
+                                         const gchar *message)
1efd99
+{
1efd99
+    GtkWidget *dialog;
1efd99
+
1efd99
+    g_warn_if_fail(message != NULL);
1efd99
+
1efd99
+    dialog = gtk_message_dialog_new(GTK_WINDOW(self),
1efd99
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1efd99
+                                    GTK_MESSAGE_ERROR,
1efd99
+                                    GTK_BUTTONS_CLOSE,
1efd99
+                                    message ? message : _("Unspecified error"));
1efd99
+    gtk_dialog_run(GTK_DIALOG(dialog));
1efd99
+    gtk_widget_destroy(dialog);
1efd99
+}
1efd99
+
1efd99
+static void
1efd99
+ovirt_foreign_menu_iso_name_changed(OvirtForeignMenu *foreign_menu,
1efd99
+                                    GAsyncResult *result,
1efd99
+                                    RemoteViewerISOListDialog *self)
1efd99
+{
1efd99
+    RemoteViewerISOListDialogPrivate *priv = self->priv;
1efd99
+    GtkTreeModel *model = GTK_TREE_MODEL(priv->list_store);
1efd99
+    gchar *current_iso;
1efd99
+    GtkTreeIter iter;
1efd99
+    gchar *name;
1efd99
+    gboolean active, match = FALSE;
1efd99
+    GError *error = NULL;
1efd99
+
1efd99
+    /* In the case of error, don't return early, because it is necessary to
1efd99
+     * change the ISO back to the original, avoiding a possible inconsistency.
1efd99
+     */
1efd99
+    if (!ovirt_foreign_menu_set_current_iso_name_finish(foreign_menu, result, &error)) {
1efd99
+        remote_viewer_iso_list_dialog_show_error(self, error ? error->message : _("Failed to change CD"));
1efd99
+        g_clear_error(&error);
1efd99
+    }
1efd99
+
1efd99
+    if (!gtk_tree_model_get_iter_first(model, &iter))
1efd99
+        return;
1efd99
+
1efd99
+    current_iso = ovirt_foreign_menu_get_current_iso_name(foreign_menu);
1efd99
+
1efd99
+    do {
1efd99
+        gtk_tree_model_get(model, &iter,
1efd99
+                           ISO_IS_ACTIVE, &active,
1efd99
+                           ISO_NAME, &name, -1);
1efd99
+        match = (g_strcmp0(current_iso, name) == 0);
1efd99
+
1efd99
+        /* iso is not active anymore */
1efd99
+        if (active && !match) {
1efd99
+            gtk_list_store_set(priv->list_store, &iter,
1efd99
+                               ISO_IS_ACTIVE, FALSE,
1efd99
+                               FONT_WEIGHT, PANGO_WEIGHT_NORMAL, -1);
1efd99
+        } else if (match) {
1efd99
+            gtk_list_store_set(priv->list_store, &iter,
1efd99
+                               ISO_IS_ACTIVE, TRUE,
1efd99
+                               FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
1efd99
+        }
1efd99
+
1efd99
+        g_free(name);
1efd99
+    } while (gtk_tree_model_iter_next(model, &iter));
1efd99
+
1efd99
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(self), GTK_RESPONSE_NONE, TRUE);
1efd99
+    gtk_widget_set_sensitive(priv->tree_view, TRUE);
1efd99
+    g_free(current_iso);
1efd99
+}
1efd99
+
1efd99
+GtkWidget *
1efd99
+remote_viewer_iso_list_dialog_new(GtkWindow *parent, GObject *foreign_menu)
1efd99
+{
1efd99
+    GtkWidget *dialog;
1efd99
+    RemoteViewerISOListDialog *self;
1efd99
+
1efd99
+    g_return_val_if_fail(foreign_menu != NULL, NULL);
1efd99
+
1efd99
+    dialog = g_object_new(REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG,
1efd99
+                          "title", _("Change CD"),
1efd99
+                          "transient-for", parent,
1efd99
+                          "border-width", 18,
1efd99
+                          "default-width", 400,
1efd99
+                          "default-height", 300,
1efd99
+                          "foreign-menu", foreign_menu,
1efd99
+                          NULL);
1efd99
+
1efd99
+    self = REMOTE_VIEWER_ISO_LIST_DIALOG(dialog);
1efd99
+    remote_viewer_iso_list_dialog_refresh_iso_list(self);
1efd99
+    return dialog;
1efd99
+}
1efd99
diff --git a/src/remote-viewer-iso-list-dialog.h b/src/remote-viewer-iso-list-dialog.h
1efd99
new file mode 100644
1efd99
index 0000000..480777c
1efd99
--- /dev/null
1efd99
+++ b/src/remote-viewer-iso-list-dialog.h
1efd99
@@ -0,0 +1,58 @@
1efd99
+/*
1efd99
+ * Virt Viewer: A virtual machine console viewer
1efd99
+ *
1efd99
+ * Copyright (C) 2017 Red Hat, Inc.
1efd99
+ *
1efd99
+ * This program is free software; you can redistribute it and/or modify
1efd99
+ * it under the terms of the GNU General Public License as published by
1efd99
+ * the Free Software Foundation; either version 2 of the License, or
1efd99
+ * (at your option) any later version.
1efd99
+ *
1efd99
+ * This program is distributed in the hope that it will be useful,
1efd99
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1efd99
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1efd99
+ * GNU General Public License for more details.
1efd99
+ *
1efd99
+ * You should have received a copy of the GNU General Public License
1efd99
+ * along with this program; if not, write to the Free Software
1efd99
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1efd99
+ */
1efd99
+
1efd99
+#ifndef __REMOTE_VIEWER_ISO_LIST_DIALOG_H__
1efd99
+#define __REMOTE_VIEWER_ISO_LIST_DIALOG_H__
1efd99
+
1efd99
+#include <gtk/gtk.h>
1efd99
+
1efd99
+G_BEGIN_DECLS
1efd99
+
1efd99
+#define REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG remote_viewer_iso_list_dialog_get_type()
1efd99
+
1efd99
+#define REMOTE_VIEWER_ISO_LIST_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG, RemoteViewerISOListDialog))
1efd99
+#define REMOTE_VIEWER_ISO_LIST_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG, RemoteViewerISOListDialogClass))
1efd99
+#define REMOTE_VIEWER_IS_ISO_LIST_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG))
1efd99
+#define REMOTE_VIEWER_IS_ISO_LIST_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG))
1efd99
+#define REMOTE_VIEWER_ISO_LIST_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), REMOTE_VIEWER_TYPE_ISO_LIST_DIALOG, RemoteViewerISOListDialogClass))
1efd99
+
1efd99
+typedef struct _RemoteViewerISOListDialog RemoteViewerISOListDialog;
1efd99
+typedef struct _RemoteViewerISOListDialogClass RemoteViewerISOListDialogClass;
1efd99
+typedef struct _RemoteViewerISOListDialogPrivate RemoteViewerISOListDialogPrivate;
1efd99
+
1efd99
+struct _RemoteViewerISOListDialog
1efd99
+{
1efd99
+    GtkDialog parent;
1efd99
+
1efd99
+    RemoteViewerISOListDialogPrivate *priv;
1efd99
+};
1efd99
+
1efd99
+struct _RemoteViewerISOListDialogClass
1efd99
+{
1efd99
+    GtkDialogClass parent_class;
1efd99
+};
1efd99
+
1efd99
+GType remote_viewer_iso_list_dialog_get_type(void) G_GNUC_CONST;
1efd99
+
1efd99
+GtkWidget *remote_viewer_iso_list_dialog_new(GtkWindow *parent, GObject *foreign_menu);
1efd99
+
1efd99
+G_END_DECLS
1efd99
+
1efd99
+#endif /* __REMOTE_VIEWER_ISO_LIST_DIALOG_H__ */
1efd99
diff --git a/src/resources/ui/remote-viewer-iso-list.ui b/src/resources/ui/remote-viewer-iso-list.ui
1efd99
new file mode 100644
1efd99
index 0000000..ab1bdc4
1efd99
--- /dev/null
1efd99
+++ b/src/resources/ui/remote-viewer-iso-list.ui
1efd99
@@ -0,0 +1,158 @@
1efd99
+
1efd99
+
1efd99
+<interface>
1efd99
+  <requires lib="gtk+" version="3.16"/>
1efd99
+  <object class="GtkListStore" id="liststore">
1efd99
+    <columns>
1efd99
+      
1efd99
+      <column type="gboolean"/>
1efd99
+      
1efd99
+      <column type="gchararray"/>
1efd99
+      
1efd99
+      <column type="gint"/>
1efd99
+    </columns>
1efd99
+  </object>
1efd99
+  <object class="GtkStack" id="stack">
1efd99
+    <property name="visible">True</property>
1efd99
+    <property name="can_focus">False</property>
1efd99
+    <child>
1efd99
+      <object class="GtkBox">
1efd99
+        <property name="visible">True</property>
1efd99
+        <property name="can_focus">False</property>
1efd99
+        <property name="orientation">vertical</property>
1efd99
+        <property name="spacing">6</property>
1efd99
+        <child>
1efd99
+          <object class="GtkLabel" id="status">
1efd99
+            <property name="visible">True</property>
1efd99
+            <property name="can_focus">False</property>
1efd99
+            <property name="label" translatable="yes">Loading...</property>
1efd99
+            <property name="yalign">1</property>
1efd99
+            <attributes>
1efd99
+              <attribute name="weight" value="bold"/>
1efd99
+            </attributes>
1efd99
+          </object>
1efd99
+          <packing>
1efd99
+            <property name="expand">True</property>
1efd99
+            <property name="fill">True</property>
1efd99
+            <property name="position">0</property>
1efd99
+          </packing>
1efd99
+        </child>
1efd99
+        <child>
1efd99
+          <object class="GtkSpinner" id="spinner">
1efd99
+            <property name="visible">True</property>
1efd99
+            <property name="can_focus">False</property>
1efd99
+            <property name="active">True</property>
1efd99
+          </object>
1efd99
+          <packing>
1efd99
+            <property name="expand">False</property>
1efd99
+            <property name="fill">True</property>
1efd99
+            <property name="position">1</property>
1efd99
+          </packing>
1efd99
+        </child>
1efd99
+        <child>
1efd99
+          <object class="GtkLabel">
1efd99
+            <property name="visible">True</property>
1efd99
+            <property name="can_focus">False</property>
1efd99
+          </object>
1efd99
+          <packing>
1efd99
+            <property name="expand">True</property>
1efd99
+            <property name="fill">True</property>
1efd99
+            <property name="position">2</property>
1efd99
+          </packing>
1efd99
+        </child>
1efd99
+      </object>
1efd99
+      <packing>
1efd99
+        <property name="name">status</property>
1efd99
+      </packing>
1efd99
+    </child>
1efd99
+    <child>
1efd99
+      <object class="GtkBox">
1efd99
+        <property name="visible">True</property>
1efd99
+        <property name="can_focus">False</property>
1efd99
+        <property name="orientation">vertical</property>
1efd99
+        <property name="spacing">6</property>
1efd99
+        <child>
1efd99
+          <object class="GtkLabel">
1efd99
+            <property name="visible">True</property>
1efd99
+            <property name="can_focus">False</property>
1efd99
+            <property name="label" translatable="yes">Select ISO</property>
1efd99
+            <property name="xalign">0</property>
1efd99
+            <attributes>
1efd99
+              <attribute name="weight" value="bold"/>
1efd99
+            </attributes>
1efd99
+          </object>
1efd99
+          <packing>
1efd99
+            <property name="expand">False</property>
1efd99
+            <property name="fill">True</property>
1efd99
+            <property name="position">0</property>
1efd99
+          </packing>
1efd99
+        </child>
1efd99
+        <child>
1efd99
+          <object class="GtkAlignment" id="alignment">
1efd99
+            <property name="visible">True</property>
1efd99
+            <property name="can_focus">False</property>
1efd99
+            <property name="bottom_padding">6</property>
1efd99
+            <child>
1efd99
+              <object class="GtkScrolledWindow">
1efd99
+                <property name="visible">True</property>
1efd99
+                <property name="can_focus">True</property>
1efd99
+                <property name="shadow_type">in</property>
1efd99
+                <child>
1efd99
+                  <object class="GtkTreeView" id="view">
1efd99
+                    <property name="visible">True</property>
1efd99
+                    <property name="can_focus">True</property>
1efd99
+                    <property name="model">liststore</property>
1efd99
+                    <property name="headers_visible">False</property>
1efd99
+                    <property name="rules_hint">True</property>
1efd99
+                    <property name="search_column">1</property>
1efd99
+                    <property name="enable_grid_lines">horizontal</property>
1efd99
+                    <signal name="row-activated" handler="remote_viewer_iso_list_dialog_row_activated" swapped="no"/>
1efd99
+                    <child internal-child="selection">
1efd99
+                      <object class="GtkTreeSelection" id="treeview-selection"/>
1efd99
+                    </child>
1efd99
+                    <child>
1efd99
+                      <object class="GtkTreeViewColumn" id="selected_column">
1efd99
+                        <property name="sizing">autosize</property>
1efd99
+                        <property name="title" translatable="yes">Selected</property>
1efd99
+                        <child>
1efd99
+                          <object class="GtkCellRendererToggle" id="cellrenderertoggle">
1efd99
+                            <signal name="toggled" handler="remote_viewer_iso_list_dialog_toggled" swapped="no"/>
1efd99
+                          </object>
1efd99
+                          <attributes>
1efd99
+                            <attribute name="active">0</attribute>
1efd99
+                          </attributes>
1efd99
+                        </child>
1efd99
+                      </object>
1efd99
+                    </child>
1efd99
+                    <child>
1efd99
+                      <object class="GtkTreeViewColumn" id="name_column">
1efd99
+                        <property name="title" translatable="yes">Name</property>
1efd99
+                        <property name="expand">True</property>
1efd99
+                        <child>
1efd99
+                          <object class="GtkCellRendererText" id="cellrenderertext"/>
1efd99
+                          <attributes>
1efd99
+                            <attribute name="text">1</attribute>
1efd99
+                            <attribute name="weight">2</attribute>
1efd99
+                          </attributes>
1efd99
+                        </child>
1efd99
+                      </object>
1efd99
+                    </child>
1efd99
+                  </object>
1efd99
+                </child>
1efd99
+              </object>
1efd99
+            </child>
1efd99
+          </object>
1efd99
+          <packing>
1efd99
+            <property name="expand">True</property>
1efd99
+            <property name="fill">True</property>
1efd99
+            <property name="position">1</property>
1efd99
+          </packing>
1efd99
+        </child>
1efd99
+      </object>
1efd99
+      <packing>
1efd99
+        <property name="name">iso-list</property>
1efd99
+        <property name="position">1</property>
1efd99
+      </packing>
1efd99
+    </child>
1efd99
+  </object>
1efd99
+</interface>
1efd99
diff --git a/src/resources/virt-viewer.gresource.xml b/src/resources/virt-viewer.gresource.xml
1efd99
index f9b4a9f..334fa47 100644
1efd99
--- a/src/resources/virt-viewer.gresource.xml
1efd99
+++ b/src/resources/virt-viewer.gresource.xml
1efd99
@@ -2,6 +2,7 @@
1efd99
 <gresources>
1efd99
   <gresource prefix="/org/virt-manager/virt-viewer">
1efd99
     <file>ui/remote-viewer-connect.ui</file>
1efd99
+    <file>ui/remote-viewer-iso-list.ui</file>
1efd99
     <file>ui/virt-viewer-about.ui</file>
1efd99
     <file>ui/virt-viewer-auth.ui</file>
1efd99
     <file>ui/virt-viewer-guest-details.ui</file>
1efd99
-- 
1efd99
2.12.0
1efd99