Blame SOURCES/0045-ovirt-Add-OvirtForeignMenu-class.patch

4ab194
From 790fa32424a9192e8d9feb3c501c7159e746e881 Mon Sep 17 00:00:00 2001
4ab194
From: Christophe Fergeau <cfergeau@redhat.com>
4ab194
Date: Fri, 27 Sep 2013 13:17:55 +0200
4ab194
Subject: [PATCH] ovirt: Add OvirtForeignMenu class
4ab194
4ab194
This class is used to implement the so-called oVirt 'foreign menu'
4ab194
which is a menu populated with ISO images available on the
4ab194
oVirt instance that the user can dynamically insert into the
4ab194
virtual machine he is currently viewing.
4ab194
4ab194
Resolves: rhbz#1127156
4ab194
(cherry picked from commit 894396d6b5e80ead9f04c28b859a111ed5b27ba5)
4ab194
---
4ab194
 configure.ac             |   3 +-
4ab194
 src/Makefile.am          |   4 +
4ab194
 src/ovirt-foreign-menu.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++
4ab194
 src/ovirt-foreign-menu.h |  82 ++++++
4ab194
 4 files changed, 774 insertions(+), 1 deletion(-)
4ab194
 create mode 100644 src/ovirt-foreign-menu.c
4ab194
 create mode 100644 src/ovirt-foreign-menu.h
4ab194
4ab194
diff --git a/configure.ac b/configure.ac
4ab194
index f966688..0515b37 100644
4ab194
--- a/configure.ac
4ab194
+++ b/configure.ac
4ab194
@@ -21,7 +21,7 @@ GTK_VNC1_REQUIRED="0.3.8"
4ab194
 GTK_VNC2_REQUIRED="0.4.0"
4ab194
 SPICE_GTK_REQUIRED="0.22"
4ab194
 SPICE_PROTOCOL_REQUIRED="0.10.1"
4ab194
-GOVIRT_REQUIRED="0.3.0"
4ab194
+GOVIRT_REQUIRED="0.3.1"
4ab194
 
4ab194
 AC_SUBST([GLIB2_REQUIRED])
4ab194
 AC_SUBST([LIBXML2_REQUIRED])
4ab194
@@ -200,6 +200,7 @@ AS_IF([test "x$have_ovirt" = "xyes"],
4ab194
              [AC_MSG_ERROR([oVirt support requested but libgovirt not found])
4ab194
       ])
4ab194
 ])
4ab194
+AM_CONDITIONAL([HAVE_OVIRT], [test "x$have_ovirt" = "xyes"])
4ab194
 
4ab194
 dnl Decide if this platform can support the SSH tunnel feature.
4ab194
 AC_CHECK_HEADERS([sys/socket.h sys/un.h windows.h])
4ab194
diff --git a/src/Makefile.am b/src/Makefile.am
4ab194
index d4163c8..c425522 100644
4ab194
--- a/src/Makefile.am
4ab194
+++ b/src/Makefile.am
4ab194
@@ -68,6 +68,10 @@ COMMON_SOURCES +=						\
4ab194
 	$(NULL)
4ab194
 endif
4ab194
 
4ab194
+if HAVE_OVIRT
4ab194
+COMMON_SOURCES +=					\
4ab194
+	ovirt-foreign-menu.h ovirt-foreign-menu.c
4ab194
+endif
4ab194
 
4ab194
 if HAVE_LIBVIRT
4ab194
 bin_PROGRAMS += virt-viewer
4ab194
diff --git a/src/ovirt-foreign-menu.c b/src/ovirt-foreign-menu.c
4ab194
new file mode 100644
4ab194
index 0000000..0453ca1
4ab194
--- /dev/null
4ab194
+++ b/src/ovirt-foreign-menu.c
4ab194
@@ -0,0 +1,686 @@
4ab194
+/*
4ab194
+ * Virt Viewer: A virtual machine console viewer
4ab194
+ *
4ab194
+ * Copyright (C) 2007-2014 Red Hat, Inc.
4ab194
+ * Copyright (C) 2009-2012 Daniel P. Berrange
4ab194
+ * Copyright (C) 2010 Marc-André Lureau
4ab194
+ *
4ab194
+ * This program is free software; you can redistribute it and/or modify
4ab194
+ * it under the terms of the GNU General Public License as published by
4ab194
+ * the Free Software Foundation; either version 2 of the License, or
4ab194
+ * (at your option) any later version.
4ab194
+ *
4ab194
+ * This program is distributed in the hope that it will be useful,
4ab194
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4ab194
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4ab194
+ * GNU General Public License for more details.
4ab194
+ *
4ab194
+ * You should have received a copy of the GNU General Public License
4ab194
+ * along with this program; if not, write to the Free Software
4ab194
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4ab194
+ *
4ab194
+ * Author: Daniel P. Berrange <berrange@redhat.com>
4ab194
+ *         Christope Fergeau <cfergeau@redhat.com>
4ab194
+ */
4ab194
+
4ab194
+#include <config.h>
4ab194
+
4ab194
+#include "ovirt-foreign-menu.h"
4ab194
+#include "virt-glib-compat.h"
4ab194
+#include "virt-viewer-util.h"
4ab194
+
4ab194
+typedef enum {
4ab194
+    STATE_0,
4ab194
+    STATE_STORAGE_DOMAIN,
4ab194
+    STATE_VM_CDROM,
4ab194
+    STATE_CDROM_FILE,
4ab194
+    STATE_ISOS
4ab194
+} OvirtForeignMenuState;
4ab194
+
4ab194
+static void ovirt_foreign_menu_next_async_step(OvirtForeignMenu *menu, OvirtForeignMenuState state);
4ab194
+static void ovirt_foreign_menu_fetch_storage_domain_async(OvirtForeignMenu *menu);
4ab194
+static void ovirt_foreign_menu_fetch_vm_cdrom_async(OvirtForeignMenu *menu);
4ab194
+static void ovirt_foreign_menu_refresh_cdrom_file_async(OvirtForeignMenu *menu);
4ab194
+static gboolean ovirt_foreign_menu_refresh_iso_list(gpointer user_data);
4ab194
+
4ab194
+G_DEFINE_TYPE (OvirtForeignMenu, ovirt_foreign_menu, G_TYPE_OBJECT)
4ab194
+
4ab194
+
4ab194
+struct _OvirtForeignMenuPrivate {
4ab194
+    OvirtProxy *proxy;
4ab194
+    OvirtApi *api;
4ab194
+    OvirtVm *vm;
4ab194
+
4ab194
+    OvirtCollection *files;
4ab194
+    OvirtCdrom *cdrom;
4ab194
+
4ab194
+    /* The next 2 members are used when changing the ISO image shown in
4ab194
+     * a VM */
4ab194
+    /* Name of the ISO which is currently used by the VM OvirtCdrom */
4ab194
+    char *current_iso_name;
4ab194
+    /* Name of the ISO we are trying to insert in the VM OvirtCdrom */
4ab194
+    char *next_iso_name;
4ab194
+
4ab194
+    GList *iso_names;
4ab194
+};
4ab194
+
4ab194
+
4ab194
+#define OVIRT_FOREIGN_MENU_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), OVIRT_TYPE_FOREIGN_MENU, OvirtForeignMenuPrivate))
4ab194
+
4ab194
+
4ab194
+enum {
4ab194
+    PROP_0,
4ab194
+    PROP_PROXY,
4ab194
+    PROP_API,
4ab194
+    PROP_VM,
4ab194
+    PROP_FILE,
4ab194
+    PROP_FILES,
4ab194
+};
4ab194
+
4ab194
+
4ab194
+static char *
4ab194
+ovirt_foreign_menu_get_current_iso_name(OvirtForeignMenu *foreign_menu)
4ab194
+{
4ab194
+    char *name;
4ab194
+
4ab194
+    if (foreign_menu->priv->cdrom == NULL) {
4ab194
+        return NULL;
4ab194
+    }
4ab194
+
4ab194
+    g_object_get(foreign_menu->priv->cdrom, "file", &name, NULL);
4ab194
+
4ab194
+    return name;
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_get_property(GObject *object, guint property_id,
4ab194
+                                       GValue *value, GParamSpec *pspec)
4ab194
+{
4ab194
+    OvirtForeignMenu *self = OVIRT_FOREIGN_MENU(object);
4ab194
+    OvirtForeignMenuPrivate *priv = self->priv;
4ab194
+
4ab194
+    switch (property_id) {
4ab194
+    case PROP_PROXY:
4ab194
+        g_value_set_object(value, priv->proxy);
4ab194
+        break;
4ab194
+    case PROP_API:
4ab194
+        g_value_set_object(value, priv->api);
4ab194
+        break;
4ab194
+    case PROP_VM:
4ab194
+        g_value_set_object(value, priv->vm);
4ab194
+        break;
4ab194
+    case PROP_FILE:
4ab194
+        g_value_take_string(value,
4ab194
+                            ovirt_foreign_menu_get_current_iso_name(self));
4ab194
+        break;
4ab194
+    case PROP_FILES:
4ab194
+        g_value_set_pointer(value, priv->iso_names);
4ab194
+        break;
4ab194
+    default:
4ab194
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_set_property(GObject *object, guint property_id,
4ab194
+                                       const GValue *value G_GNUC_UNUSED, GParamSpec *pspec)
4ab194
+{
4ab194
+    OvirtForeignMenu *self = OVIRT_FOREIGN_MENU(object);
4ab194
+    OvirtForeignMenuPrivate *priv = self->priv;
4ab194
+
4ab194
+    switch (property_id) {
4ab194
+    case PROP_PROXY:
4ab194
+        if (priv->proxy != NULL) {
4ab194
+            g_object_unref(priv->proxy);
4ab194
+        }
4ab194
+        priv->proxy = g_value_dup_object(value);
4ab194
+        break;
4ab194
+    case PROP_API:
4ab194
+        if (priv->api != NULL) {
4ab194
+            g_object_unref(priv->api);
4ab194
+        }
4ab194
+        priv->api = g_value_dup_object(value);
4ab194
+        break;
4ab194
+    case PROP_VM:
4ab194
+        if (priv->vm != NULL) {
4ab194
+            g_object_unref(priv->vm);
4ab194
+        }
4ab194
+        priv->vm = g_value_dup_object(value);
4ab194
+        break;
4ab194
+    default:
4ab194
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_dispose(GObject *obj)
4ab194
+{
4ab194
+    OvirtForeignMenu *self = OVIRT_FOREIGN_MENU(obj);
4ab194
+
4ab194
+    if (self->priv->proxy) {
4ab194
+        g_object_unref(self->priv->proxy);
4ab194
+        self->priv->proxy = NULL;
4ab194
+    }
4ab194
+
4ab194
+    if (self->priv->api != NULL) {
4ab194
+        g_object_unref(self->priv->api);
4ab194
+        self->priv->api = NULL;
4ab194
+    }
4ab194
+
4ab194
+    if (self->priv->vm) {
4ab194
+        g_object_unref(self->priv->vm);
4ab194
+        self->priv->vm = NULL;
4ab194
+    }
4ab194
+
4ab194
+    if (self->priv->files) {
4ab194
+        g_object_unref(self->priv->files);
4ab194
+        self->priv->files = NULL;
4ab194
+    }
4ab194
+
4ab194
+    if (self->priv->cdrom) {
4ab194
+        g_object_unref(self->priv->cdrom);
4ab194
+        self->priv->cdrom = NULL;
4ab194
+    }
4ab194
+
4ab194
+    if (self->priv->iso_names) {
4ab194
+        g_list_free_full(self->priv->iso_names, (GDestroyNotify)g_free);
4ab194
+        self->priv->iso_names = NULL;
4ab194
+    }
4ab194
+
4ab194
+    g_free(self->priv->current_iso_name);
4ab194
+    self->priv->current_iso_name = NULL;
4ab194
+
4ab194
+    g_free(self->priv->next_iso_name);
4ab194
+    self->priv->next_iso_name = NULL;
4ab194
+
4ab194
+    G_OBJECT_CLASS(ovirt_foreign_menu_parent_class)->dispose(obj);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_class_init(OvirtForeignMenuClass *klass)
4ab194
+{
4ab194
+    GObjectClass *oclass = G_OBJECT_CLASS(klass);
4ab194
+
4ab194
+    oclass->get_property = ovirt_foreign_menu_get_property;
4ab194
+    oclass->set_property = ovirt_foreign_menu_set_property;
4ab194
+    oclass->dispose = ovirt_foreign_menu_dispose;
4ab194
+
4ab194
+    g_type_class_add_private(klass, sizeof(OvirtForeignMenuPrivate));
4ab194
+
4ab194
+    g_object_class_install_property(oclass,
4ab194
+                                    PROP_PROXY,
4ab194
+                                    g_param_spec_object("proxy",
4ab194
+                                                        "OvirtProxy instance",
4ab194
+                                                        "OvirtProxy instance",
4ab194
+                                                        OVIRT_TYPE_PROXY,
4ab194
+                                                        G_PARAM_READWRITE |
4ab194
+                                                        G_PARAM_CONSTRUCT_ONLY |
4ab194
+                                                        G_PARAM_STATIC_STRINGS));
4ab194
+    g_object_class_install_property(oclass,
4ab194
+                                    PROP_API,
4ab194
+                                    g_param_spec_object("api",
4ab194
+                                                        "OvirtApi instance",
4ab194
+                                                        "Ovirt api root",
4ab194
+                                                        OVIRT_TYPE_API,
4ab194
+                                                        G_PARAM_READWRITE |
4ab194
+                                                        G_PARAM_STATIC_STRINGS));
4ab194
+    g_object_class_install_property(oclass,
4ab194
+                                    PROP_VM,
4ab194
+                                    g_param_spec_object("vm",
4ab194
+                                                        "OvirtVm instance",
4ab194
+                                                        "OvirtVm being handled",
4ab194
+                                                        OVIRT_TYPE_VM,
4ab194
+                                                        G_PARAM_READWRITE |
4ab194
+                                                        G_PARAM_STATIC_STRINGS));
4ab194
+    g_object_class_install_property(oclass,
4ab194
+                                    PROP_FILE,
4ab194
+                                    g_param_spec_string("file",
4ab194
+                                                         "File",
4ab194
+                                                         "Name of the image currently inserted in the virtual CDROM",
4ab194
+                                                         NULL,
4ab194
+                                                         G_PARAM_READABLE |
4ab194
+                                                         G_PARAM_STATIC_STRINGS));
4ab194
+    g_object_class_install_property(oclass,
4ab194
+                                    PROP_FILES,
4ab194
+                                    g_param_spec_pointer("files",
4ab194
+                                                         "ISO names",
4ab194
+                                                         "GSList of ISO names for this oVirt VM",
4ab194
+                                                         G_PARAM_READABLE |
4ab194
+                                                         G_PARAM_STATIC_STRINGS));
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_init(OvirtForeignMenu *self)
4ab194
+{
4ab194
+    self->priv = OVIRT_FOREIGN_MENU_GET_PRIVATE(self);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+OvirtForeignMenu* ovirt_foreign_menu_new(OvirtProxy *proxy)
4ab194
+{
4ab194
+    return g_object_new(OVIRT_TYPE_FOREIGN_MENU,
4ab194
+                        "proxy", proxy,
4ab194
+                        NULL);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_next_async_step(OvirtForeignMenu *menu,
4ab194
+                                   OvirtForeignMenuState current_state)
4ab194
+{
4ab194
+    g_return_if_fail(current_state >= STATE_0);
4ab194
+    g_return_if_fail(current_state < STATE_ISOS);
4ab194
+
4ab194
+    current_state++;
4ab194
+
4ab194
+    if (current_state == STATE_STORAGE_DOMAIN) {
4ab194
+        if (menu->priv->files == NULL) {
4ab194
+            ovirt_foreign_menu_fetch_storage_domain_async(menu);
4ab194
+        } else {
4ab194
+            current_state++;
4ab194
+        }
4ab194
+    }
4ab194
+
4ab194
+    if (current_state == STATE_VM_CDROM) {
4ab194
+        if (menu->priv->cdrom == NULL) {
4ab194
+            ovirt_foreign_menu_fetch_vm_cdrom_async(menu);
4ab194
+        } else {
4ab194
+            current_state++;
4ab194
+        }
4ab194
+    }
4ab194
+
4ab194
+    if (current_state == STATE_CDROM_FILE) {
4ab194
+        ovirt_foreign_menu_refresh_cdrom_file_async(menu);
4ab194
+    }
4ab194
+
4ab194
+    if (current_state == STATE_ISOS) {
4ab194
+        g_warn_if_fail(menu->priv->api != NULL);
4ab194
+        g_warn_if_fail(menu->priv->vm != NULL);
4ab194
+        g_warn_if_fail(menu->priv->files != NULL);
4ab194
+        g_warn_if_fail(menu->priv->cdrom != NULL);
4ab194
+
4ab194
+        ovirt_foreign_menu_refresh_iso_list(menu);
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+void
4ab194
+ovirt_foreign_menu_start(OvirtForeignMenu *menu)
4ab194
+{
4ab194
+    ovirt_foreign_menu_next_async_step(menu, STATE_0);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_activate_item_cb(GtkMenuItem *menuitem, gpointer user_data);
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+menu_item_set_active_no_signal(GtkMenuItem *menuitem,
4ab194
+                               gboolean active,
4ab194
+                               GCallback callback,
4ab194
+                               gpointer user_data)
4ab194
+{
4ab194
+    g_signal_handlers_block_by_func(menuitem, callback, user_data);
4ab194
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), active);
4ab194
+    g_signal_handlers_unblock_by_func(menuitem, callback, user_data);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void updated_cdrom_cb(GObject *source_object,
4ab194
+                             GAsyncResult *result,
4ab194
+                             G_GNUC_UNUSED gpointer user_data)
4ab194
+{
4ab194
+    GError *error = NULL;
4ab194
+    OvirtForeignMenu *foreign_menu;
4ab194
+    gboolean updated;
4ab194
+
4ab194
+    foreign_menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    updated = ovirt_cdrom_update_finish(OVIRT_CDROM(source_object),
4ab194
+                                        result, &error);
4ab194
+    g_debug("Finished updating cdrom content");
4ab194
+    if (updated) {
4ab194
+        g_free(foreign_menu->priv->current_iso_name);
4ab194
+        foreign_menu->priv->current_iso_name = foreign_menu->priv->next_iso_name;
4ab194
+        foreign_menu->priv->next_iso_name = NULL;
4ab194
+        g_object_notify(G_OBJECT(foreign_menu), "file");
4ab194
+    } else {
4ab194
+        /* Reset old state back as we were not successful in switching to
4ab194
+         * the new ISO */
4ab194
+        const char *current_file = foreign_menu->priv->current_iso_name;
4ab194
+
4ab194
+        if (error != NULL) {
4ab194
+            g_warning("failed to update cdrom resource: %s", error->message);
4ab194
+            g_clear_error(&error);
4ab194
+        }
4ab194
+        g_debug("setting OvirtCdrom:file back to '%s'",
4ab194
+                current_file?current_file:NULL);
4ab194
+        g_object_set(foreign_menu->priv->cdrom, "file", current_file, NULL);
4ab194
+    }
4ab194
+    g_free(foreign_menu->priv->next_iso_name);
4ab194
+    foreign_menu->priv->next_iso_name = NULL;
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void
4ab194
+ovirt_foreign_menu_activate_item_cb(GtkMenuItem *menuitem, gpointer user_data)
4ab194
+{
4ab194
+    OvirtForeignMenu *foreign_menu;
4ab194
+    const char *iso_name;
4ab194
+    gboolean checked;
4ab194
+
4ab194
+    checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
4ab194
+    foreign_menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    g_return_if_fail(foreign_menu->priv->cdrom != NULL);
4ab194
+    g_return_if_fail(foreign_menu->priv->next_iso_name == NULL);
4ab194
+
4ab194
+    g_debug("'%s' clicked", gtk_menu_item_get_label(menuitem));
4ab194
+
4ab194
+    /* We only want to move the check mark for the currently selected ISO
4ab194
+     * when ovirt_cdrom_update_async() is successful, so for now we move
4ab194
+     * the check mark back to where it was before
4ab194
+     */
4ab194
+    menu_item_set_active_no_signal(menuitem, !checked,
4ab194
+                                   (GCallback)ovirt_foreign_menu_activate_item_cb,
4ab194
+                                   foreign_menu);
4ab194
+
4ab194
+    if (checked) {
4ab194
+        iso_name = gtk_menu_item_get_label(menuitem);
4ab194
+        g_debug("Updating VM cdrom image to '%s'", iso_name);
4ab194
+        foreign_menu->priv->next_iso_name = g_strdup(iso_name);
4ab194
+    } else {
4ab194
+        g_debug("Removing current cdrom image");
4ab194
+        iso_name = NULL;
4ab194
+        foreign_menu->priv->next_iso_name = NULL;
4ab194
+        /* FIXME: No idea how to remove a CDRom from a VM through ovirt REST
4ab194
+         * API for now, so return early.
4ab194
+         */
4ab194
+        return;
4ab194
+    }
4ab194
+    g_object_set(foreign_menu->priv->cdrom,
4ab194
+                 "file", iso_name,
4ab194
+                 NULL);
4ab194
+    ovirt_cdrom_update_async(foreign_menu->priv->cdrom, TRUE,
4ab194
+                             foreign_menu->priv->proxy, NULL,
4ab194
+                             updated_cdrom_cb, foreign_menu);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+GtkWidget *ovirt_foreign_menu_get_gtk_menu(OvirtForeignMenu *foreign_menu)
4ab194
+{
4ab194
+    GtkWidget *gtk_menu;
4ab194
+    GList *it;
4ab194
+    char *current_iso;
4ab194
+
4ab194
+    g_debug("Creating GtkMenu for foreign menu");
4ab194
+    current_iso = ovirt_foreign_menu_get_current_iso_name(foreign_menu);
4ab194
+    gtk_menu = gtk_menu_new();
4ab194
+    for (it = foreign_menu->priv->iso_names; it != NULL; it = it->next) {
4ab194
+        GtkWidget *menuitem;
4ab194
+
4ab194
+        menuitem = gtk_check_menu_item_new_with_label((char *)it->data);
4ab194
+        if (g_strcmp0((char *)it->data, current_iso) == 0) {
4ab194
+            g_warn_if_fail(g_strcmp0(current_iso, foreign_menu->priv->current_iso_name) == 0);
4ab194
+            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
4ab194
+                                           TRUE);
4ab194
+        }
4ab194
+        g_signal_connect(menuitem, "activate",
4ab194
+                         G_CALLBACK(ovirt_foreign_menu_activate_item_cb),
4ab194
+                         foreign_menu);
4ab194
+        gtk_menu_shell_append(GTK_MENU_SHELL(gtk_menu), menuitem);
4ab194
+    }
4ab194
+    g_free(current_iso);
4ab194
+
4ab194
+    return gtk_menu;
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void ovirt_foreign_menu_set_files(OvirtForeignMenu *menu,
4ab194
+                                         const GList *files)
4ab194
+{
4ab194
+    GList *sorted_files = NULL;
4ab194
+    const GList *it;
4ab194
+    GList *it2;
4ab194
+
4ab194
+    for (it = files; it != NULL; it = it->next) {
4ab194
+        char *name;
4ab194
+        g_object_get(it->data, "name", &name, NULL);
4ab194
+        /* The oVirt REST API is supposed to have a 'type' node
4ab194
+         * associated with file resources , but as of 3.2, this node
4ab194
+         * is not present, so we do an extension check instead
4ab194
+         * to differentiate between ISOs and floppy images */
4ab194
+        if (g_str_has_suffix(name, ".vfd")) {
4ab194
+            g_free(name);
4ab194
+            continue;
4ab194
+        }
4ab194
+        sorted_files = g_list_insert_sorted(sorted_files, name,
4ab194
+                                            (GCompareFunc)g_strcmp0);
4ab194
+    }
4ab194
+
4ab194
+    for (it = sorted_files, it2 = menu->priv->iso_names;
4ab194
+         (it != NULL) && (it2 != NULL);
4ab194
+         it = it->next, it2 = it2->next) {
4ab194
+        if (g_strcmp0(it->data, it2->data) != 0) {
4ab194
+            break;
4ab194
+        }
4ab194
+    }
4ab194
+
4ab194
+    if ((it == NULL) && (it2 == NULL)) {
4ab194
+        /* sorted_files and menu->priv->files content was the same */
4ab194
+        g_list_free_full(sorted_files, (GDestroyNotify)g_free);
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    g_list_free_full(menu->priv->iso_names, (GDestroyNotify)g_free);
4ab194
+    menu->priv->iso_names = sorted_files;
4ab194
+    g_object_notify(G_OBJECT(menu), "files");
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void cdrom_file_refreshed_cb(GObject *source_object,
4ab194
+                                    GAsyncResult *result,
4ab194
+                                    gpointer user_data)
4ab194
+{
4ab194
+    OvirtResource *cdrom  = OVIRT_RESOURCE(source_object);
4ab194
+    OvirtForeignMenu *menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    GError *error = NULL;
4ab194
+
4ab194
+    ovirt_resource_refresh_finish(cdrom, result, &error);
4ab194
+    if (error != NULL) {
4ab194
+        g_warning("failed to refresh cdrom content: %s", error->message);
4ab194
+        g_clear_error(&error);
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    /* Content of OvirtCdrom is now current */
4ab194
+    g_free(menu->priv->current_iso_name);
4ab194
+    if (menu->priv->cdrom != NULL) {
4ab194
+        g_object_get(G_OBJECT(menu->priv->cdrom),
4ab194
+                     "file", &menu->priv->current_iso_name,
4ab194
+                     NULL);
4ab194
+    } else {
4ab194
+        menu->priv->current_iso_name = NULL;
4ab194
+    }
4ab194
+    g_object_notify(G_OBJECT(menu), "file");
4ab194
+    if (menu->priv->cdrom != NULL) {
4ab194
+        ovirt_foreign_menu_next_async_step(menu, STATE_CDROM_FILE);
4ab194
+    } else {
4ab194
+        g_debug("Could not find VM cdrom through oVirt REST API");
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void ovirt_foreign_menu_refresh_cdrom_file_async(OvirtForeignMenu *menu)
4ab194
+{
4ab194
+    g_return_if_fail(OVIRT_IS_RESOURCE(menu->priv->cdrom));
4ab194
+
4ab194
+    ovirt_resource_refresh_async(OVIRT_RESOURCE(menu->priv->cdrom),
4ab194
+                                 menu->priv->proxy, NULL,
4ab194
+                                 cdrom_file_refreshed_cb, menu);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void cdroms_fetched_cb(GObject *source_object,
4ab194
+                              GAsyncResult *result,
4ab194
+                              gpointer user_data)
4ab194
+{
4ab194
+    GHashTable *cdroms;
4ab194
+    OvirtCollection *cdrom_collection = OVIRT_COLLECTION(source_object);
4ab194
+    OvirtForeignMenu *menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    GHashTableIter iter;
4ab194
+    OvirtCdrom *cdrom;
4ab194
+    GError *error = NULL;
4ab194
+
4ab194
+    ovirt_collection_fetch_finish(cdrom_collection, result, &error);
4ab194
+    if (error != NULL) {
4ab194
+        g_warning("failed to fetch cdrom collection: %s", error->message);
4ab194
+        g_clear_error(&error);
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    cdroms = ovirt_collection_get_resources(cdrom_collection);
4ab194
+
4ab194
+    g_warn_if_fail(g_hash_table_size(cdroms) <= 1);
4ab194
+
4ab194
+    g_hash_table_iter_init(&iter, cdroms);
4ab194
+    /* Set CDROM drive. If we have multiple ones, only the first
4ab194
+     * one will be kept, but currently oVirt only adds one CDROM
4ab194
+     * device per-VM
4ab194
+     */
4ab194
+    if (g_hash_table_iter_next(&iter, NULL, (gpointer *)&cdrom)) {
4ab194
+        if (menu->priv->cdrom != NULL) {
4ab194
+            g_object_unref(G_OBJECT(menu->priv->cdrom));
4ab194
+        }
4ab194
+        menu->priv->cdrom = g_object_ref(G_OBJECT(cdrom));
4ab194
+        g_debug("Set VM cdrom to %p", menu->priv->cdrom);
4ab194
+    }
4ab194
+
4ab194
+    if (menu->priv->cdrom != NULL) {
4ab194
+        ovirt_foreign_menu_next_async_step(menu, STATE_VM_CDROM);
4ab194
+    } else {
4ab194
+        g_debug("Could not find VM cdrom through oVirt REST API");
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void ovirt_foreign_menu_fetch_vm_cdrom_async(OvirtForeignMenu *menu)
4ab194
+{
4ab194
+    OvirtCollection *cdrom_collection;
4ab194
+
4ab194
+    cdrom_collection = ovirt_vm_get_cdroms(menu->priv->vm);
4ab194
+    ovirt_collection_fetch_async(cdrom_collection, menu->priv->proxy, NULL,
4ab194
+                                 cdroms_fetched_cb, menu);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void storage_domains_fetched_cb(GObject *source_object,
4ab194
+                                       GAsyncResult *result,
4ab194
+                                       gpointer user_data)
4ab194
+{
4ab194
+    GError *error = NULL;
4ab194
+    OvirtForeignMenu *menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    OvirtCollection *collection = OVIRT_COLLECTION(source_object);
4ab194
+    GHashTableIter iter;
4ab194
+    OvirtStorageDomain *domain;
4ab194
+
4ab194
+    ovirt_collection_fetch_finish(collection, result, &error);
4ab194
+    if (error != NULL) {
4ab194
+        g_warning("failed to fetch storage domains: %s", error->message);
4ab194
+        g_clear_error(&error);
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    g_hash_table_iter_init(&iter, ovirt_collection_get_resources(collection));
4ab194
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&domain)) {
4ab194
+        OvirtCollection *file_collection;
4ab194
+        int type;
4ab194
+
4ab194
+        g_object_get(domain, "type", &type, NULL);
4ab194
+        if (type != OVIRT_STORAGE_DOMAIN_TYPE_ISO) {
4ab194
+            continue;
4ab194
+        }
4ab194
+
4ab194
+        file_collection = ovirt_storage_domain_get_files(domain);
4ab194
+        if (file_collection != NULL) {
4ab194
+            if (menu->priv->files) {
4ab194
+                g_object_unref(G_OBJECT(menu->priv->files));
4ab194
+            }
4ab194
+            menu->priv->files = g_object_ref(G_OBJECT(file_collection));
4ab194
+            g_debug("Set VM files to %p", menu->priv->files);
4ab194
+            break;
4ab194
+        }
4ab194
+    }
4ab194
+
4ab194
+    if (menu->priv->files != NULL) {
4ab194
+        ovirt_foreign_menu_next_async_step(menu, STATE_STORAGE_DOMAIN);
4ab194
+    } else {
4ab194
+        g_debug("Could not find iso file collection");
4ab194
+    }
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void ovirt_foreign_menu_fetch_storage_domain_async(OvirtForeignMenu *menu)
4ab194
+{
4ab194
+    OvirtCollection *collection;
4ab194
+
4ab194
+    g_debug("Start fetching oVirt REST collection");
4ab194
+    collection = ovirt_api_get_storage_domains(menu->priv->api);
4ab194
+    ovirt_collection_fetch_async(collection, menu->priv->proxy, NULL,
4ab194
+                                 storage_domains_fetched_cb, menu);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void iso_list_fetched_cb(GObject *source_object,
4ab194
+                                GAsyncResult *result,
4ab194
+                                gpointer user_data)
4ab194
+{
4ab194
+    OvirtCollection *collection = OVIRT_COLLECTION(source_object);
4ab194
+    GError *error = NULL;
4ab194
+    GList *files;
4ab194
+
4ab194
+    ovirt_collection_fetch_finish(collection, result, &error);
4ab194
+    if (error != NULL) {
4ab194
+        g_warning("failed to fetch files for ISO storage domain: %s",
4ab194
+                   error->message);
4ab194
+        g_clear_error(&error);
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    files = g_hash_table_get_values(ovirt_collection_get_resources(collection));
4ab194
+    ovirt_foreign_menu_set_files(OVIRT_FOREIGN_MENU(user_data), files);
4ab194
+    g_list_free(files);
4ab194
+
4ab194
+    g_timeout_add_seconds(15, ovirt_foreign_menu_refresh_iso_list, user_data);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static void ovirt_foreign_menu_fetch_iso_list_async(OvirtForeignMenu *menu)
4ab194
+{
4ab194
+    if (menu->priv->files == NULL) {
4ab194
+        return;
4ab194
+    }
4ab194
+
4ab194
+    ovirt_collection_fetch_async(menu->priv->files, menu->priv->proxy,
4ab194
+                                 NULL, iso_list_fetched_cb, menu);
4ab194
+}
4ab194
+
4ab194
+
4ab194
+static gboolean ovirt_foreign_menu_refresh_iso_list(gpointer user_data)
4ab194
+{
4ab194
+    OvirtForeignMenu *menu;
4ab194
+
4ab194
+    g_debug("Refreshing foreign menu iso list");
4ab194
+    menu = OVIRT_FOREIGN_MENU(user_data);
4ab194
+    ovirt_foreign_menu_fetch_iso_list_async(menu);
4ab194
+
4ab194
+    /* ovirt_foreign_menu_fetch_iso_list_async() will schedule a new call to
4ab194
+     * that function through iso_list_fetched_cb() when it has finished
4ab194
+     * fetching the iso list
4ab194
+     */
4ab194
+    return G_SOURCE_REMOVE;
4ab194
+}
4ab194
diff --git a/src/ovirt-foreign-menu.h b/src/ovirt-foreign-menu.h
4ab194
new file mode 100644
4ab194
index 0000000..7d28f0b
4ab194
--- /dev/null
4ab194
+++ b/src/ovirt-foreign-menu.h
4ab194
@@ -0,0 +1,82 @@
4ab194
+/*
4ab194
+ * Virt Viewer: A virtual machine console viewer
4ab194
+ *
4ab194
+ * Copyright (C) 2007-2014 Red Hat, Inc.
4ab194
+ * Copyright (C) 2009-2012 Daniel P. Berrange
4ab194
+ * Copyright (C) 2010 Marc-André Lureau
4ab194
+ *
4ab194
+ * This program is free software; you can redistribute it and/or modify
4ab194
+ * it under the terms of the GNU General Public License as published by
4ab194
+ * the Free Software Foundation; either version 2 of the License, or
4ab194
+ * (at your option) any later version.
4ab194
+ *
4ab194
+ * This program is distributed in the hope that it will be useful,
4ab194
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4ab194
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4ab194
+ * GNU General Public License for more details.
4ab194
+ *
4ab194
+ * You should have received a copy of the GNU General Public License
4ab194
+ * along with this program; if not, write to the Free Software
4ab194
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4ab194
+ *
4ab194
+ * Author: Daniel P. Berrange <berrange@redhat.com>
4ab194
+ *         Christophe Fergeau <cfergeau@redhat.com>
4ab194
+ */
4ab194
+#ifndef _OVIRT_FOREIGN_MENU_H
4ab194
+#define _OVIRT_FOREIGN_MENU_H
4ab194
+
4ab194
+#include <glib-object.h>
4ab194
+#include <govirt/govirt.h>
4ab194
+#include <gtk/gtk.h>
4ab194
+
4ab194
+
4ab194
+G_BEGIN_DECLS
4ab194
+
4ab194
+#define OVIRT_TYPE_FOREIGN_MENU ovirt_foreign_menu_get_type()
4ab194
+
4ab194
+#define OVIRT_FOREIGN_MENU(obj)                                        \
4ab194
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), OVIRT_TYPE_FOREIGN_MENU, OvirtForeignMenu))
4ab194
+
4ab194
+#define OVIRT_FOREIGN_MENU_CLASS(klass)                                \
4ab194
+    (G_TYPE_CHECK_CLASS_CAST ((klass), OVIRT_TYPE_FOREIGN_MENU, OvirtForeignMenuClass))
4ab194
+
4ab194
+#define OVIRT_IS_FOREIGN_MENU(obj)                                \
4ab194
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OVIRT_TYPE_FOREIGN_MENU))
4ab194
+
4ab194
+#define OVIRTIS_FOREIGN_MENU_CLASS(klass)                        \
4ab194
+    (G_TYPE_CHECK_CLASS_TYPE ((klass), OVIRT_TYPE_FOREIGN_MENU))
4ab194
+
4ab194
+#define OVIRT_FOREIGN_MENU_GET_CLASS(obj)                        \
4ab194
+    (G_TYPE_INSTANCE_GET_CLASS ((obj), OVIRT_TYPE_FOREIGN_MENU, OvirtForeignMenuClass))
4ab194
+
4ab194
+typedef struct _OvirtForeignMenu OvirtForeignMenu;
4ab194
+typedef struct _OvirtForeignMenuClass OvirtForeignMenuClass;
4ab194
+typedef struct _OvirtForeignMenuPrivate OvirtForeignMenuPrivate;
4ab194
+
4ab194
+struct _OvirtForeignMenu {
4ab194
+    GObject parent;
4ab194
+
4ab194
+    OvirtForeignMenuPrivate *priv;
4ab194
+};
4ab194
+
4ab194
+struct _OvirtForeignMenuClass {
4ab194
+    GObjectClass parent_class;
4ab194
+};
4ab194
+
4ab194
+GType ovirt_foreign_menu_get_type(void);
4ab194
+
4ab194
+OvirtForeignMenu* ovirt_foreign_menu_new(OvirtProxy *proxy);
4ab194
+void ovirt_foreign_menu_start(OvirtForeignMenu *menu);
4ab194
+
4ab194
+GtkWidget *ovirt_foreign_menu_get_gtk_menu(OvirtForeignMenu *foreign_menu);
4ab194
+
4ab194
+G_END_DECLS
4ab194
+
4ab194
+#endif /* _OVIRT_FOREIGN_MENU_H */
4ab194
+/*
4ab194
+ * Local variables:
4ab194
+ *  c-indent-level: 4
4ab194
+ *  c-basic-offset: 4
4ab194
+ *  indent-tabs-mode: nil
4ab194
+ * End:
4ab194
+ */