<div dir="ltr">Hi<br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Sep 19, 2018 at 6:43 PM, Marc-André Lureau <span dir="ltr"><<a href="mailto:marcandre.lureau@gmail.com" target="_blank">marcandre.lureau@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi<br>
<br>
On Tue, Sep 18, 2018 at 8:05 PM Yuri Benditovich<br>
<div><div class="m_-3734118308932770974m_-1352333094766519113m_39639117952959130m_-3776099943896030855m_-15508575401570799m_-4751879021535101723m_-5710700431131011310m_-6775554001762653852h5"><<a href="mailto:yuri.benditovich@daynix.com" target="_blank">yuri.benditovich@daynix.com</a>> wrote:<br>
><br>
><br>
><br>
> On Tue, Sep 18, 2018 at 12:15 PM, Marc-André Lureau <<a href="mailto:marcandre.lureau@gmail.com" target="_blank">marcandre.lureau@gmail.com</a>> wrote:<br>
>><br>
>> Hi<br>
>><br>
>> On Tue, Sep 18, 2018 at 12:04 PM Yuri Benditovich<br>
>> <<a href="mailto:yuri.benditovich@daynix.com" target="_blank">yuri.benditovich@daynix.com</a>> wrote:<br>
>> ><br>
>> ><br>
>> ><br>
>> > On Tue, Sep 18, 2018 at 9:10 AM, Victor Toso <<a href="mailto:victortoso@redhat.com" target="_blank">victortoso@redhat.com</a>> wrote:<br>
>> >><br>
>> >> On Mon, Sep 17, 2018 at 04:23:02PM +0300, Yuri Benditovich wrote:<br>
>> >> > New USB widget supports all the functionality related to<br>
>> >> > redirection of local USB device (as previous usb widget did).<br>
>> >> > Additionally it allows creation, management and redirection of<br>
>> >> > emulated USB CD devices for CD sharing.<br>
>> >><br>
>> >> Wouldn't be possible to extend the current widget instead of<br>
>> >> creating a new one?<br>
>> ><br>
>> ><br>
>> > Of course it was the first idea before creating new one.<br>
>> > After several discussions we understood that there is almost nothing<br>
>> > common between what we'd like to have for CD and what we have in existing widget.<br>
>> > This also was decided on special meeting of Alexander with spice team (remove<br>
>> > some controls and split it to two lists, locals and CD). Earlier the screenshot of<br>
>> > the latest widget (current code) was sent to internal list for review and no objections<br>
>> > were received.<br>
>><br>
>> Ultimately the discussion and decision should take place on public mailing list.<br>
>><br>
>> And many of us (including me), may not be directly involved in the RH<br>
>> spice team.<br>
>><br>
>> Could you detail the goals you had and why you had to write a new widget?<br>
><br>
><br>
> Regarding new USB widget:<br>
><br>
> User experience when USB CD drive is presented to user is wider than just<br>
> plug/unplug the USB device.<br>
><br>
> The CD may include several units, each of them can be managed separately.<br>
> If more than one unit supported on CD, single CD with number of units requires<br>
> just one redirection channel. [At the moment due to time restrictions, we define<br>
> CD as single unit, but I think in the future we will want to enable multiple units on CD]<br>
> So in common case the GUI for CD drives tends to be something like tree view.<br>
> This easily serves also local USB devices as in GTK tree view == list view.<br>
> Initially the widget was developed with single list containing all the devices<br>
> when shared CD devices have leaves and local USB devices do not.<br>
> IMHO, this kind of presentation is better than current one which presents local devices<br>
> and CD devices separately.<br>
<br>
</div></div>Ok, let say I am biased, and I have opinions because I would use it<br>
and this is stuff I am also maintaining:<br>
<br>
- I have never used a pluggable CD drive.<br>
<br>
- I have never used mulitple CD drive simultaneously.<br></blockquote><div><br></div><div>This is common case when you install Windows VM with virtio kind of hard drive</div><div>Typically you load 2 ISOs (OS and guest tools)</div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span><br>
> When CD drive is presented to the user on guest machine, it can be ejected by<br>
> user action (from Gnome or Explorer). When the media is ejected, it can be changed<br>
> on client side. So the status (ejected/loaded) should be visible and the user should be<br>
> able to control this also on client side exactly as you can open/close the drive door manually.<br>
><br>
> Drive lock. When CD is reqired for SW operation, the SW often locks<br>
> the drive preventing user-initiated eject. In such case, if needed, the CD still can be<br>
> manually ejected using clip. Another option related to 'lock' is that user may want<br>
> to prevent removal of CD by guest software and lock it on client side.<br>
<br>
</span>- I have never used locking either.<br></blockquote><div><br></div><div>You did not, of couse. This is software-driven operation, SW does it by sending ioctls.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span><br>
><br>
> The user shall select the properties for logical CD unit and it is good to show them<br>
> in the GUI. They include (at least):<br>
> * file name (or device name) used as media<br>
<br>
</span>That's useful indeed<br>
<span><br>
> * device attributes (vendor/product) which are used to report properties of SCSI device<br>
> (these properties are visible to user in seabios boot screen, so in case of 2 CDs the user<br>
> can recognize the needed one). Note that 2 CDs is typical case for Windows installation<br>
> on VM with virtio.<br>
<br>
</span>You can switch the mounted CD for this case, no need for multiple<br>
simultaneously mounted CD. I don't know a case where this is<br>
necessary, except perhaps for direct CD copy, that used to be very<br>
risky anyway..<br></blockquote><div><br></div><div>Use case: installing Windows 7/10 VM ( or Win 7 ) with virtio hard drive.</div><div>Need to share 2 ISOs (this is what users do, this allows install all the drivers just</div><div>after OS installation). The user needs to distinguish between them in boot screen.</div><div>See attached bitmap of boot with 2 qemu ISO images, where</div><div>the minimalistic gui causes the user question himself which one to select;</div><div>if he selects wrong one, the boot fails.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
> * Whether the CD media is currently loaded<br>
><br>
> Visible status of the device is (IMHO) good for self support. When something does not<br>
> work as expected and you can't see anything in the GUI that can explain this, the next step<br>
> is opening issues, sending logs etc., i.e. spend own time and time of other people.<br>
> I disagree with the statement 'less GUI is better'. Definitely, less GUI is simpler,<br>
> but this simplicity often increases cost of ownership.<br>
<br>
</span>The UI in Boxes is doing the job quite fine for average user. See<br>
attached screenshot.<br>
<span><br></span></blockquote><div><br></div><div>Honestly, I'm not impressed by this gui.</div><div>There are 2 approaches: "do as less as possible" and</div><div>"do a little more". I am on the second one, because when</div><div>'something more' required it is typically already done.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
><br>
> Our solid opinion is that even current CD additions are too heavy for existing widget.<br>
<br>
</span>In my opinion, we can make it much more user friendly. But we need to<br>
understand what we want to support first. Why spice-gtk cd redirection<br>
would need to be more complex than what Boxes or virt-manager provide?<br>
<div><div class="m_-3734118308932770974m_-1352333094766519113m_39639117952959130m_-3776099943896030855m_-15508575401570799m_-4751879021535101723m_-5710700431131011310m_-6775554001762653852h5"><br></div></div></blockquote><div><br></div><div>This is exactly the problem in this conversation.</div><div>I do not think this gui is more complicated, I think it is more reach, clear and informative.</div><div>Indeed, 'Address' field can be removed, it does not help too much.</div><div>'Vendor' and 'Product' fields are exactly device properties visible</div><div>to the user on guest side (for example, by lsusb and lsscsi) or in Windows</div><div>device manager.</div><div>'Connected' shows exact status of the device, when the check box</div><div>'Redirect' shows the intention. With local device this can take time until</div><div>the device is reset, reenumerated and redirected.</div><div>'Loaded' shows exact status of the device and gives a hint when the CD</div><div>is not visible in guests explorer.</div><div>I still think we will want to enable multiple units per CD in future, so we avoid</div><div>binding 'eject' to device removal (this can be done for single-unit CD).</div><div>'Lock' allows also to create non-ejectable device (attempt to eject</div><div>on guest side will fail). So, IMO we provide just more features and</div><div>appropriately richer GUI.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="m_-3734118308932770974m_-1352333094766519113m_39639117952959130m_-3776099943896030855m_-15508575401570799m_-4751879021535101723m_-5710700431131011310m_-6775554001762653852h5">
><br>
> Thanks,<br>
> Yuri<br>
><br>
>><br>
>><br>
>> For UI, it's best to focus on the supported use case & the user<br>
>> story/experience, and avoid feature creep (usually the less UI, the<br>
>> better).<br>
>><br>
>> ><br>
>> >><br>
>> >><br>
>> >> > Signed-off-by: Alexander Nezhinsky <<a href="mailto:alexander@daynix.com" target="_blank">alexander@daynix.com</a>><br>
>> >> > Signed-off-by: Yuri Benditovich <<a href="mailto:yuri.benditovich@daynix.com" target="_blank">yuri.benditovich@daynix.com</a>><br>
>> >> > ---<br>
>> >> >  src/usb-device-redir-widget.c | 2065 ++++++++++++++++++++++++++++++<wbr>+++++++++++<br>
>> >> >  1 file changed, 2065 insertions(+)<br>
>> >> >  create mode 100644 src/usb-device-redir-widget.c<br>
>> >> ><br>
>> >> > diff --git a/src/usb-device-redir-widget.<wbr>c b/src/usb-device-redir-widget.<wbr>c<br>
>> >> > new file mode 100644<br>
>> >> > index 0000000..59a6043<br>
>> >> > --- /dev/null<br>
>> >> > +++ b/src/usb-device-redir-widget.<wbr>c<br>
>> >> > @@ -0,0 +1,2065 @@<br>
>> >> > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */<br>
>> >> > +/*<br>
>> >> > +   Copyright (C) 2012 Red Hat, Inc.<br>
>> >> > +<br>
>> >> > +   Red Hat Authors:<br>
>> >> > +   Alexander Nezhinsky<<a href="mailto:anezhins@redhat.com" target="_blank">anezhins@redhat.com</a>><br>
>> >> > +<br>
>> >> > +   This library is free software; you can redistribute it and/or<br>
>> >> > +   modify it under the terms of the GNU Lesser General Public<br>
>> >> > +   License as published by the Free Software Foundation; either<br>
>> >> > +   version 2.1 of the License, or (at your option) any later version.<br>
>> >> > +<br>
>> >> > +   This library is distributed in the hope that it will be useful,<br>
>> >> > +   but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
>> >> > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
>> >> > +   Lesser General Public License for more details.<br>
>> >> > +<br>
>> >> > +   You should have received a copy of the GNU Lesser General Public<br>
>> >> > +   License along with this library; if not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
>> >> > +*/<br>
>> >> > +<br>
>> >> > +#include "config.h"<br>
>> >> > +#ifndef USB_WIDGET_TEST<br>
>> >> > +    #include <glib/gi18n-lib.h><br>
>> >> > +    #include "spice-client.h"<br>
>> >> > +    #include "spice-marshal.h"<br>
>> >> > +#else<br>
>> >> > +    #include "spice-client.h"<br>
>> >> > +#endif<br>
>> >> > +#include "usb-device-widget.h"<br>
>> >> > +<br>
>> >> > +/*<br>
>> >> > +    Debugging note:<br>
>> >> > +    Logging from this module is not affected by --spice-debug<br>
>> >> > +    command line parameter<br>
>> >> > +    Use SPICE_DEBUG=1 environment varible to enable logs<br>
>> >> > +*/<br>
>> >> > +<br>
>> >> > +#ifdef USE_NEW_USB_WIDGET<br>
>> >> > +<br>
>> >> > +/**<br>
>> >> > + * SECTION:usb-device-widget<br>
>> >> > + * @short_description: USB device selection widget<br>
>> >> > + * @title: Spice USB device selection widget<br>
>> >> > + * @section_id:<br>
>> >> > + * @see_also:<br>
>> >> > + * @stability: Under development<br>
>> >> > + * @include: spice-client-gtk.h<br>
>> >> > + *<br>
>> >> > + * #SpiceUsbDeviceWidget is a gtk widget which apps can use to easily<br>
>> >> > + * add an UI to select USB devices to redirect (or unredirect).<br>
>> >> > + */<br>
>> >> > +<br>
>> >> > +struct _SpiceUsbDeviceWidget<br>
>> >> > +{<br>
>> >> > +    GtkBox parent;<br>
>> >> > +<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv;<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +struct _SpiceUsbDeviceWidgetClass<br>
>> >> > +{<br>
>> >> > +    GtkBoxClass parent_class;<br>
>> >> > +<br>
>> >> > +    /* signals */<br>
>> >> > +    void (*connect_failed) (SpiceUsbDeviceWidget *widget,<br>
>> >> > +                            SpiceUsbDevice *device, GError *error);<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +/* ------------------------------<wbr>------------------------------<wbr>------ */<br>
>> >> > +/* Prototypes for callbacks  */<br>
>> >> > +static void device_added_cb(SpiceUsbDevice<wbr>Manager *manager,<br>
>> >> > +    SpiceUsbDevice *device, gpointer user_data);<br>
>> >> > +static void device_removed_cb(SpiceUsbDevi<wbr>ceManager *manager,<br>
>> >> > +    SpiceUsbDevice *device, gpointer user_data);<br>
>> >> > +static void device_changed_cb(SpiceUsbDevi<wbr>ceManager *manager,<br>
>> >> > +    SpiceUsbDevice *device, gpointer user_data);<br>
>> >> > +static void device_error_cb(SpiceUsbDevice<wbr>Manager *manager,<br>
>> >> > +    SpiceUsbDevice *device, GError *err, gpointer user_data);<br>
>> >> > +static gboolean spice_usb_device_widget_update<wbr>_status(gpointer user_data);<br>
>> >> > +<br>
>> >> > +/* ------------------------------<wbr>------------------------------<wbr>------ */<br>
>> >> > +/* gobject glue                                                       */<br>
>> >> > +<br>
>> >> > +#define SPICE_USB_DEVICE_WIDGET_GET_PR<wbr>IVATE(obj) \<br>
>> >> > +    (G_TYPE_INSTANCE_GET_PRIVATE((<wbr>obj), SPICE_TYPE_USB_DEVICE_WIDGET, \<br>
>> >> > +                                 SpiceUsbDeviceWidgetPrivate))<br>
>> >> > +<br>
>> >> > +enum {<br>
>> >> > +    PROP_0,<br>
>> >> > +    PROP_SESSION,<br>
>> >> > +    PROP_DEVICE_FORMAT_STRING,<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +enum {<br>
>> >> > +    CONNECT_FAILED,<br>
>> >> > +    LAST_SIGNAL,<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +typedef struct {<br>
>> >> > +    GtkTreeView *tree_view;<br>
>> >> > +    GtkTreeStore *tree_store;<br>
>> >> > +} SpiceUsbDeviceWidgetTree;<br>
>> >> > +<br>
>> >> > +struct _SpiceUsbDeviceWidgetPrivate {<br>
>> >> > +    SpiceSession *session;<br>
>> >> > +    gchar *device_format_string;<br>
>> >> > +    SpiceUsbDeviceManager *manager;<br>
>> >> > +    GtkWidget *info_bar;<br>
>> >> > +    GtkWidget *label;<br>
>> >> > +    SpiceUsbDeviceWidgetTree cd_tree;<br>
>> >> > +    SpiceUsbDeviceWidgetTree usb_tree;<br>
>> >> > +    GdkPixbuf *icon_cd;<br>
>> >> > +    GdkPixbuf *icon_connected;<br>
>> >> > +    GdkPixbuf *icon_disconn;<br>
>> >> > +    GdkPixbuf *icon_warning;<br>
>> >> > +    GdkPixbuf *icon_info;<br>
>> >> > +    gchar *err_msg;<br>
>> >> > +    gsize device_count;<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +static guint signals[LAST_SIGNAL] = { 0, };<br>
>> >> > +<br>
>> >> > +G_DEFINE_TYPE(SpiceUsbDeviceW<wbr>idget, spice_usb_device_widget, GTK_TYPE_BOX);<br>
>> >> > +<br>
>> >> > +/* TREE */<br>
>> >> > +<br>
>> >> > +enum column_id<br>
>> >> > +{<br>
>> >> > +    COL_REDIRECT = 0,<br>
>> >> > +    COL_ADDRESS,<br>
>> >> > +    COL_CONNECT_ICON,<br>
>> >> > +    COL_CD_ICON,<br>
>> >> > +    COL_VENDOR,<br>
>> >> > +    COL_PRODUCT,<br>
>> >> > +    COL_FILE,<br>
>> >> > +    COL_LOADED,<br>
>> >> > +    COL_LOCKED,<br>
>> >> > +    COL_IDLE,<br>
>> >> > +    /* internal columns */<br>
>> >> > +    COL_REVISION,<br>
>> >> > +    COL_CD_DEV,<br>
>> >> > +    COL_LUN_ITEM,<br>
>> >> > +    COL_DEV_ITEM,<br>
>> >> > +    COL_ITEM_DATA,<br>
>> >> > +    COL_CONNECTED,<br>
>> >> > +    COL_CAN_REDIRECT,<br>
>> >> > +    COL_ROW_COLOR,<br>
>> >> > +    COL_ROW_COLOR_SET,<br>
>> >> > +    NUM_COLS,<br>
>> >> > +<br>
>> >> > +    INVALID_COL<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +// there is a possibility to use different names<br>
>> >> > +// for columns in USB device list and CD device list, if needed<br>
>> >> > +// currently they are identical<br>
>> >> > +static const char *column_names_cd[NUM_COLS];<br>
>> >> > +static const char *column_names_usb[NUM_COLS];<br>
>> >> > +<br>
>> >> > +#define SET_COLUMN(n, s) column_names_cd[n] = column_names_usb[n] = s<br>
>> >> > +<br>
>> >> > +static void initialize_columns(void)<br>
>> >> > +{<br>
>> >> > +    SET_COLUMN(COL_REDIRECT, _("Redirect"));<br>
>> >> > +    SET_COLUMN(COL_ADDRESS, _("Address"));<br>
>> >> > +    SET_COLUMN(COL_CONNECT_ICON, _("Conn"));<br>
>> >> > +    SET_COLUMN(COL_CD_ICON, _("CD"));<br>
>> >> > +    SET_COLUMN(COL_VENDOR, _("Vendor"));<br>
>> >> > +    SET_COLUMN(COL_PRODUCT, _("Product"));<br>
>> >> > +    SET_COLUMN(COL_FILE, _("File/Device Path"));<br>
>> >> > +    SET_COLUMN(COL_LOADED, _("Loaded"));<br>
>> >> > +    SET_COLUMN(COL_LOCKED, _("Locked"));<br>
>> >> > +    SET_COLUMN(COL_IDLE, _("Idle"));<br>
>> >> > +    SET_COLUMN(COL_REVISION, "?Revision");<br>
>> >> > +    SET_COLUMN(COL_CD_DEV, "?CD_DEV");<br>
>> >> > +    SET_COLUMN(COL_LUN_ITEM, "?LUN_ITEM");<br>
>> >> > +    SET_COLUMN(COL_DEV_ITEM, "?DEV_ITEM");<br>
>> >> > +    SET_COLUMN(COL_ITEM_DATA, "?ITEM_DATA");<br>
>> >> > +    SET_COLUMN(COL_CONNECTED, "?CONNECTED");<br>
>> >> > +    SET_COLUMN(COL_CAN_REDIRECT, "?CAN_REDIRECT");<br>
>> >> > +    SET_COLUMN(COL_ROW_COLOR, "?ROW_COLOR");<br>
>> >> > +    SET_COLUMN(COL_ROW_COLOR_SET, "?ROW_COLOR_SET");<br>
>> >> > +};<br>
>> >> > +<br>
>> >> > +static const char *column_name(enum column_id id, gboolean is_cd)<br>
>> >> > +{<br>
>> >> > +    const char **col_name = is_cd ? column_names_cd : column_names_usb;<br>
>> >> > +    return col_name[id];<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +typedef struct _UsbWidgetLunItem {<br>
>> >> > +    SpiceUsbDeviceManager *manager;<br>
>> >> > +    SpiceUsbDevice *device;<br>
>> >> > +    guint lun;<br>
>> >> > +    SpiceUsbDeviceLunInfo info;<br>
>> >> > +} UsbWidgetLunItem;<br>
>> >> > +<br>
>> >> > +typedef struct _TreeFindUsbDev {<br>
>> >> > +    SpiceUsbDevice *usb_dev;<br>
>> >> > +    GtkTreeIter dev_iter;<br>
>> >> > +} TreeFindUsbDev;<br>
>> >> > +<br>
>> >> > +typedef void (*tree_item_toggled_cb)(GtkCel<wbr>lRendererToggle *, gchar *, gpointer);<br>
>> >> > +<br>
>> >> > +static void usb_widget_add_device(SpiceUsb<wbr>DeviceWidget *self,<br>
>> >> > +                                          SpiceUsbDevice *usb_device,<br>
>> >> > +                                          GtkTreeIter *old_dev_iter);<br>
>> >> > +<br>
>> >> > +static gchar *usb_device_description(SpiceU<wbr>sbDeviceManager *manager,<br>
>> >> > +                                     SpiceUsbDevice *device,<br>
>> >> > +                                     const gchar *format)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceDescription desc;<br>
>> >> > +    gchar *descriptor;<br>
>> >> > +    gchar *res;<br>
>> >> > +    spice_usb_device_get_info(mana<wbr>ger, device, &desc);<br>
>> >> > +    descriptor = g_strdup_printf("[%04x:%04x]", desc.vendor_id, desc.product_id);<br>
>> >> > +    if (!format) {<br>
>> >> > +        format = _("%s %s %s at %d-%d");<br>
>> >> > +    }<br>
>> >> > +    res = g_strdup_printf(format, desc.vendor, desc.product, descriptor, desc.bus, desc.address);<br>
>> >> > +    g_free(desc.vendor);<br>
>> >> > +    g_free(desc.product);<br>
>> >> > +    g_free(descriptor);<br>
>> >> > +    return res;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeStore* usb_widget_create_tree_store(v<wbr>oid)<br>
>> >> > +{<br>
>> >> > +    GtkTreeStore *tree_store;<br>
>> >> > +<br>
>> >> > +    tree_store = gtk_tree_store_new(NUM_COLS,<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_REDIRECT */<br>
>> >> > +                        G_TYPE_STRING, /* COL_ADDRESS */<br>
>> >> > +                        GDK_TYPE_PIXBUF, /* COL_CONNECT_ICON */<br>
>> >> > +                        GDK_TYPE_PIXBUF, /* COL_CD_ICON */<br>
>> >> > +                        G_TYPE_STRING, /* COL_VENDOR */<br>
>> >> > +                        G_TYPE_STRING, /* COL_PRODUCT */<br>
>> >> > +                        G_TYPE_STRING, /* COL_FILE */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_LOADED */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_LOCKED */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_IDLE */<br>
>> >> > +                        /* internal columns */<br>
>> >> > +                        G_TYPE_STRING, /* COL_REVISION */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_CD_DEV */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_LUN_ITEM */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_DEV_ITEM */<br>
>> >> > +                        G_TYPE_POINTER, /* COL_ITEM_DATA */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_CONNECTED */<br>
>> >> > +                        G_TYPE_BOOLEAN, /* COL_CAN_REDIRECT */<br>
>> >> > +                        G_TYPE_STRING, /* COL_ROW_COLOR */<br>
>> >> > +                        G_TYPE_BOOLEAN /* COL_ROW_COLOR_SET */ );<br>
>> >> > +    SPICE_DEBUG("tree store created");<br>
>> >> > +<br>
>> >> > +    return tree_store;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GdkPixbuf *get_named_icon(const gchar *name, gint size)<br>
>> >> > +{<br>
>> >> > +    GtkIconInfo *info = gtk_icon_theme_lookup_icon(gtk<wbr>_icon_theme_get_default(), name, size, 0);<br>
>> >> > +    GdkPixbuf *pixbuf = gtk_icon_info_load_icon(info, NULL);<br>
>> >> > +    g_object_unref (info);<br>
>> >> > +    return pixbuf;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void select_widget_size(GtkWidget *wg)<br>
>> >> > +{<br>
>> >> > +    GdkDisplay *d = gtk_widget_get_display(wg);<br>
>> >> > +    int i, w = 2000, h = 1024;<br>
>> >> > +    int n = gdk_display_get_n_monitors(d);<br>
>> >> > +    for (i = 0; i < n; ++i)<br>
>> >> > +    {<br>
>> >> > +        GdkMonitor *m = gdk_display_get_monitor(d, i);<br>
>> >> > +        if (m) {<br>
>> >> > +            GdkRectangle area;<br>
>> >> > +            gdk_monitor_get_workarea(m, &area);<br>
>> >> > +            SPICE_DEBUG("monitor %d: %d x %d @( %d, %d)",<br>
>> >> > +                i, area.width, area.height, area.x, area.y );<br>
>> >> > +            w = MIN(w, area.width);<br>
>> >> > +            h = MIN(h, area.height);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    w = (w * 3) / 4;<br>
>> >> > +    h = h / 2;<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("sizing widget as %d x %d", w, h);<br>
>> >> > +    gtk_widget_set_size_request(wg<wbr>, w, h);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void usb_widget_add_device(SpiceUsb<wbr>DeviceWidget *self,<br>
>> >> > +                                          SpiceUsbDevice *usb_device,<br>
>> >> > +                                          GtkTreeIter *old_dev_iter)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    SpiceUsbDeviceManager *usb_dev_mgr = priv->manager;<br>
>> >> > +    SpiceUsbDeviceWidgetTree *tree;<br>
>> >> > +    GtkTreeView *tree_view;<br>
>> >> > +    GtkTreeStore *tree_store;<br>
>> >> > +    GtkTreeIter new_dev_iter;<br>
>> >> > +    SpiceUsbDeviceDescription dev_info;<br>
>> >> > +    GtkTreePath *new_dev_path;<br>
>> >> > +    gboolean is_dev_redirected, is_dev_connected, is_dev_cd;<br>
>> >> > +    gchar *addr_str;<br>
>> >> > +    GArray *lun_array;<br>
>> >> > +    guint lun_index;<br>
>> >> > +    GError *error = NULL;<br>
>> >> > +<br>
>> >> > +    is_dev_cd = spice_usb_device_manager_is_de<wbr>vice_cd(usb_dev_mgr, usb_device);<br>
>> >> > +    tree = is_dev_cd ? &priv->cd_tree : &priv->usb_tree;<br>
>> >> > +    tree_view = tree->tree_view;<br>
>> >> > +    tree_store = tree->tree_store;<br>
>> >> > +<br>
>> >> > +    if (old_dev_iter == NULL) {<br>
>> >> > +        gtk_tree_store_append(tree_sto<wbr>re, &new_dev_iter, NULL);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_tree_store_insert_after(tr<wbr>ee_store, &new_dev_iter, NULL, old_dev_iter);<br>
>> >> > +        gtk_tree_store_remove(tree_sto<wbr>re, old_dev_iter);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    spice_usb_device_get_info(usb_<wbr>dev_mgr, usb_device, &dev_info);<br>
>> >> > +    addr_str = g_strdup_printf("%d:%d", (gint)dev_info.bus, (gint)dev_info.address);<br>
>> >> > +    is_dev_connected = spice_usb_device_manager_is_de<wbr>vice_connected(usb_dev_mgr, usb_device);<br>
>> >> > +    is_dev_redirected = is_dev_connected;<br>
>> >> > +    SPICE_DEBUG("usb device a:[%s] p:[%s] m:[%s] conn:%d cd:%d",<br>
>> >> > +        addr_str, dev_info.vendor, dev_info.product, is_dev_connected, is_dev_cd);<br>
>> >> > +<br>
>> >> > +    gtk_tree_store_set(tree_store, &new_dev_iter,<br>
>> >> > +        COL_REDIRECT, is_dev_redirected,<br>
>> >> > +        COL_ADDRESS, addr_str,<br>
>> >> > +        COL_CONNECT_ICON, is_dev_connected ? priv->icon_connected : priv->icon_disconn,<br>
>> >> > +        COL_CD_ICON, priv->icon_cd,<br>
>> >> > +        COL_VENDOR, dev_info.vendor,<br>
>> >> > +        COL_PRODUCT, dev_info.product,<br>
>> >> > +        COL_CD_DEV, is_dev_cd,<br>
>> >> > +        COL_LUN_ITEM, FALSE, /* USB device item */<br>
>> >> > +        COL_DEV_ITEM, TRUE, /* USB device item */<br>
>> >> > +        COL_ITEM_DATA, (gpointer)usb_device,<br>
>> >> > +        COL_CONNECTED, is_dev_connected,<br>
>> >> > +        COL_CAN_REDIRECT, spice_usb_device_manager_can_r<wbr>edirect_device(usb_dev_mgr, usb_device, &error),<br>
>> >> > +        COL_ROW_COLOR, "beige",<br>
>> >> > +        COL_ROW_COLOR_SET, TRUE,<br>
>> >> > +        -1);<br>
>> >> > +    g_clear_error(&error);<br>
>> >> > +<br>
>> >> > +    priv->device_count++;<br>
>> >> > +<br>
>> >> > +    /* get all the luns */<br>
>> >> > +    lun_array = spice_usb_device_manager_get_d<wbr>evice_luns(usb_dev_mgr, usb_device);<br>
>> >> > +    for (lun_index = 0; lun_index < lun_array->len; lun_index++) {<br>
>> >> > +        UsbWidgetLunItem *lun_item;<br>
>> >> > +        GtkTreeIter lun_iter;<br>
>> >> > +        gchar lun_str[8];<br>
>> >> > +<br>
>> >> > +        lun_item = g_malloc(sizeof(*lun_item));<br>
>> >> > +        lun_item->manager = usb_dev_mgr;<br>
>> >> > +        lun_item->device = usb_device;<br>
>> >> > +        lun_item->lun = g_array_index(lun_array, guint, lun_index);<br>
>> >> > +        spice_usb_device_manager_devic<wbr>e_lun_get_info(usb_dev_mgr, usb_device, lun_item->lun, &lun_item->info);<br>
>> >> > +        SPICE_DEBUG("lun %u v:[%s] p:[%s] r:[%s] file:[%s] lun_item:%p",<br>
>> >> > +                lun_index, lun_item->info.vendor, lun_item->info.product,<br>
>> >> > +                lun_item->info.revision, lun_item->info.file_path, lun_item);<br>
>> >> > +        g_snprintf(lun_str, 8, "↳%u", lun_item->lun);<br>
>> >> > +<br>
>> >> > +        /* Append LUN as a child of USB device */<br>
>> >> > +        gtk_tree_store_append(tree_sto<wbr>re, &lun_iter, &new_dev_iter);<br>
>> >> > +        gtk_tree_store_set(tree_store, &lun_iter,<br>
>> >> > +                COL_ADDRESS, lun_str,<br>
>> >> > +                COL_VENDOR, lun_item->info.vendor,<br>
>> >> > +                COL_PRODUCT, lun_item->info.product,<br>
>> >> > +                COL_REVISION, lun_item->info.revision,<br>
>> >> > +                COL_FILE, lun_item->info.file_path,<br>
>> >> > +                COL_LOADED, lun_item->info.loaded,<br>
>> >> > +                COL_LOCKED, lun_item->info.locked,<br>
>> >> > +                COL_IDLE, !lun_item->info.started,<br>
>> >> > +                COL_CD_DEV, FALSE,<br>
>> >> > +                COL_LUN_ITEM, TRUE, /* LUN item */<br>
>> >> > +                COL_DEV_ITEM, FALSE, /* LUN item */<br>
>> >> > +                COL_ITEM_DATA, (gpointer)lun_item,<br>
>> >> > +                COL_CONNECTED, is_dev_connected,<br>
>> >> > +                COL_ROW_COLOR, "azure",<br>
>> >> > +                COL_ROW_COLOR_SET, TRUE,<br>
>> >> > +                -1);<br>
>> >> > +    }<br>
>> >> > +    g_array_unref(lun_array);<br>
>> >> > +<br>
>> >> > +    new_dev_path = gtk_tree_model_get_path(GTK_TR<wbr>EE_MODEL(tree_store), &new_dev_iter);<br>
>> >> > +    gtk_tree_view_expand_row(tree_<wbr>view, new_dev_path, FALSE);<br>
>> >> > +    gtk_tree_path_free(new_dev_pat<wbr>h);<br>
>> >> > +<br>
>> >> > +    g_free(dev_info.vendor);<br>
>> >> > +    g_free(dev_info.product);<br>
>> >> > +    g_free(addr_str);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static gboolean tree_item_is_lun(GtkTreeStore *tree_store, GtkTreeIter *iter)<br>
>> >> > +{<br>
>> >> > +    gboolean is_lun;<br>
>> >> > +    gtk_tree_model_get(GTK_TREE_MO<wbr>DEL(tree_store), iter, COL_LUN_ITEM, &is_lun, -1);<br>
>> >> > +    return is_lun;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static gboolean usb_widget_tree_store_find_usb<wbr>_dev_foreach_cb(GtkTreeModel *tree_model,<br>
>> >> > +                                                              GtkTreePath *path, GtkTreeIter *iter,<br>
>> >> > +                                                              gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    TreeFindUsbDev *find_info = (TreeFindUsbDev *)user_data;<br>
>> >> > +    SpiceUsbDevice *find_usb_device = find_info->usb_dev;<br>
>> >> > +    SpiceUsbDevice *usb_device;<br>
>> >> > +    gboolean is_lun_item;<br>
>> >> > +<br>
>> >> > +    gtk_tree_model_get(tree_model, iter,<br>
>> >> > +                       COL_LUN_ITEM, &is_lun_item,<br>
>> >> > +                       COL_ITEM_DATA, (gpointer *)&usb_device,<br>
>> >> > +                       -1);<br>
>> >> > +    if (!is_lun_item && usb_device == find_usb_device) {<br>
>> >> > +        find_info->dev_iter = *iter;<br>
>> >> > +        find_info->usb_dev  = NULL;<br>
>> >> > +        SPICE_DEBUG("Usb dev found %p iter %p", usb_device, iter);<br>
>> >> > +        return TRUE; /* stop iterating */<br>
>> >> > +    } else {<br>
>> >> > +        return FALSE; /* continue iterating */<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeIter *usb_widget_tree_store_find_us<wbr>b_device(GtkTreeStore *tree_store,<br>
>> >> > +                                                          SpiceUsbDevice *usb_device)<br>
>> >> > +{<br>
>> >> > +    TreeFindUsbDev find_info = { .usb_dev = usb_device,.dev_iter = {} };<br>
>> >> > +    GtkTreeIter *iter = NULL;<br>
>> >> > +<br>
>> >> > +    gtk_tree_model_foreach(GTK_TRE<wbr>E_MODEL(tree_store),<br>
>> >> > +                           usb_widget_tree_store_find_us<wbr>b_dev_foreach_cb, (gpointer)&find_info);<br>
>> >> > +    // the callback sets 'usb_dev' field to zero if it finds the device<br>
>> >> > +    if (!find_info.usb_dev) {<br>
>> >> > +        iter = g_malloc(sizeof(*iter));<br>
>> >> > +        *iter = find_info.dev_iter;<br>
>> >> > +    }<br>
>> >> > +    return iter;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static gboolean usb_widget_remove_device(Spice<wbr>UsbDeviceWidget *self,<br>
>> >> > +                                         SpiceUsbDevice *usb_device)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeIter *old_dev_iter;<br>
>> >> > +    GtkTreeStore *tree_store = priv->usb_tree.tree_store;<br>
>> >> > +    // on WIN32 it is possible the backend device is already removed<br>
>> >> > +    // from the USB device manager list and we can't know it was<br>
>> >> > +    // USB or CD, do we will try both lists<br>
>> >> > +    old_dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_device);<br>
>> >> > +    if (old_dev_iter != NULL) {<br>
>> >> > +        SPICE_DEBUG("USB Device removed");<br>
>> >> > +        gtk_tree_store_remove(tree_sto<wbr>re, old_dev_iter);<br>
>> >> > +        priv->device_count--;<br>
>> >> > +        g_free(old_dev_iter);<br>
>> >> > +        return TRUE;<br>
>> >> > +    }<br>
>> >> > +    tree_store = priv->cd_tree.tree_store;<br>
>> >> > +    if (tree_store) {<br>
>> >> > +        old_dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_device);<br>
>> >> > +    }<br>
>> >> > +    if (old_dev_iter != NULL) {<br>
>> >> > +        SPICE_DEBUG("CD Device removed");<br>
>> >> > +        gtk_tree_store_remove(tree_sto<wbr>re, old_dev_iter);<br>
>> >> > +        priv->device_count--;<br>
>> >> > +        g_free(old_dev_iter);<br>
>> >> > +        return TRUE;<br>
>> >> > +    }<br>
>> >> > +    SPICE_DEBUG("Device %p not found!", usb_device);<br>
>> >> > +    return FALSE;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeViewColumn* view_add_toggle_column(SpiceUs<wbr>bDeviceWidget *self,<br>
>> >> > +                                                 enum column_id toggle_col_id,<br>
>> >> > +                                                 enum column_id visible_col_id,<br>
>> >> > +                                                 enum column_id sensitive_col_id,<br>
>> >> > +                                                 tree_item_toggled_cb toggled_cb,<br>
>> >> > +                                                 gboolean is_cd)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkCellRenderer     *renderer;<br>
>> >> > +    GtkTreeViewColumn   *view_col;<br>
>> >> > +    const char *col_name = column_name(toggle_col_id, is_cd);<br>
>> >> > +<br>
>> >> > +    renderer = gtk_cell_renderer_toggle_new()<wbr>;<br>
>> >> > +<br>
>> >> > +    if (sensitive_col_id != INVALID_COL) {<br>
>> >> > +        view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                        col_name,<br>
>> >> > +                        renderer,<br>
>> >> > +                        "active", toggle_col_id,<br>
>> >> > +                        "visible", visible_col_id,<br>
>> >> > +                        "activatable", sensitive_col_id,<br>
>> >> > +                        NULL);<br>
>> >> > +    } else {<br>
>> >> > +        view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                        col_name,<br>
>> >> > +                        renderer,<br>
>> >> > +                        "active", toggle_col_id,<br>
>> >> > +                        "visible", visible_col_id,<br>
>> >> > +                        NULL);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    gtk_tree_view_column_set_sizin<wbr>g(view_col, GTK_TREE_VIEW_COLUMN_FIXED);<br>
>> >> > +    gtk_tree_view_column_set_expan<wbr>d(view_col, FALSE);<br>
>> >> > +    gtk_tree_view_column_set_resiz<wbr>able(view_col, FALSE);<br>
>> >> > +    gtk_tree_view_append_column(is<wbr>_cd ? priv->cd_tree.tree_view : priv->usb_tree.tree_view, view_col);<br>
>> >> > +<br>
>> >> > +    g_signal_connect(renderer, "toggled", G_CALLBACK(toggled_cb), self);<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("view added toggle column [%u : %s] visible when [%u : %s]",<br>
>> >> > +            toggle_col_id, col_name,<br>
>> >> > +            visible_col_id, column_name(visible_col_id, is_cd));<br>
>> >> > +    return view_col;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeViewColumn* view_add_read_only_toggle_colu<wbr>mn(SpiceUsbDeviceWidget *self,<br>
>> >> > +                                                           enum column_id toggle_col_id,<br>
>> >> > +                                                           enum column_id visible_col_id,<br>
>> >> > +                                                           gboolean is_cd)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkCellRenderer     *renderer;<br>
>> >> > +    GtkTreeViewColumn   *view_col;<br>
>> >> > +    const char *col_name = column_name(toggle_col_id, is_cd);<br>
>> >> > +<br>
>> >> > +    renderer = gtk_cell_renderer_toggle_new()<wbr>;<br>
>> >> > +<br>
>> >> > +    view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                    col_name,<br>
>> >> > +                    renderer,<br>
>> >> > +                    "active", toggle_col_id,<br>
>> >> > +                    "visible", visible_col_id,<br>
>> >> > +                    NULL);<br>
>> >> > +<br>
>> >> > +    gtk_cell_renderer_toggle_set_a<wbr>ctivatable(GTK_CELL_RENDERER_T<wbr>OGGLE(renderer), FALSE);<br>
>> >> > +<br>
>> >> > +    gtk_tree_view_column_set_sizin<wbr>g(view_col, GTK_TREE_VIEW_COLUMN_FIXED);<br>
>> >> > +    gtk_tree_view_column_set_expan<wbr>d(view_col, FALSE);<br>
>> >> > +    gtk_tree_view_column_set_resiz<wbr>able(view_col, FALSE);<br>
>> >> > +    gtk_tree_view_append_column(is<wbr>_cd ? priv->cd_tree.tree_view : priv->usb_tree.tree_view, view_col);<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("view added read-only toggle column [%u : %s] visible when [%u : %s]",<br>
>> >> > +            toggle_col_id, col_name,<br>
>> >> > +            visible_col_id, column_name(visible_col_id, is_cd));<br>
>> >> > +    return view_col;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeViewColumn* view_add_text_column(SpiceUsbD<wbr>eviceWidget *self,<br>
>> >> > +                                               enum column_id col_id,<br>
>> >> > +                                               gboolean expandable,<br>
>> >> > +                                               gboolean is_cd)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkCellRenderer     *renderer;<br>
>> >> > +    GtkTreeViewColumn   *view_col;<br>
>> >> > +<br>
>> >> > +    renderer = gtk_cell_renderer_text_new();<br>
>> >> > +<br>
>> >> > +    view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                    column_name(col_id, is_cd),<br>
>> >> > +                    renderer,<br>
>> >> > +                    "text", col_id,<br>
>> >> > +                    //"cell-background", COL_ROW_COLOR,<br>
>> >> > +                    //"cell-background-set", COL_ROW_COLOR_SET,<br>
>> >> > +                    NULL);<br>
>> >> > +<br>
>> >> > +    gtk_tree_view_column_set_sizin<wbr>g(view_col, GTK_TREE_VIEW_COLUMN_GROW_ONLY<wbr>);<br>
>> >> > +    gtk_tree_view_column_set_resiz<wbr>able(view_col, TRUE);<br>
>> >> > +    gtk_tree_view_column_set_expan<wbr>d(view_col, expandable);<br>
>> >> > +<br>
>> >> > +    gtk_tree_view_append_column(is<wbr>_cd ? priv->cd_tree.tree_view : priv->usb_tree.tree_view, view_col);<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("view added text column [%u : %s]", col_id, column_name(col_id, is_cd));<br>
>> >> > +    return view_col;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeViewColumn* view_add_pixbuf_column(SpiceUs<wbr>bDeviceWidget *self,<br>
>> >> > +                                                 enum column_id col_id,<br>
>> >> > +                                                 enum column_id visible_col_id,<br>
>> >> > +                                                 gboolean is_cd)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkCellRenderer     *renderer;<br>
>> >> > +    GtkTreeViewColumn   *view_col;<br>
>> >> > +    const char *col_name = column_name(col_id, is_cd);<br>
>> >> > +<br>
>> >> > +    renderer = gtk_cell_renderer_pixbuf_new()<wbr>;<br>
>> >> > +<br>
>> >> > +    if (visible_col_id == INVALID_COL) {<br>
>> >> > +        view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                        col_name,<br>
>> >> > +                        renderer,<br>
>> >> > +                        "pixbuf", col_id,<br>
>> >> > +                        NULL);<br>
>> >> > +        SPICE_DEBUG("view added pixbuf col[%u : %s] visible always", col_id, col_name);<br>
>> >> > +    } else {<br>
>> >> > +        view_col = gtk_tree_view_column_new_with_<wbr>attributes(<br>
>> >> > +                        col_name,<br>
>> >> > +                        renderer,<br>
>> >> > +                        "pixbuf", col_id,<br>
>> >> > +                        "visible", visible_col_id,<br>
>> >> > +                        NULL);<br>
>> >> > +        SPICE_DEBUG("view added pixbuf col[%u : %s] visible when[%u : %s]",<br>
>> >> > +                col_id, col_name, visible_col_id, column_name(visible_col_id, is_cd));<br>
>> >> > +    }<br>
>> >> > +    gtk_tree_view_append_column(is<wbr>_cd ? priv->cd_tree.tree_view : priv->usb_tree.tree_view, view_col);<br>
>> >> > +    return view_col;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +/* Toggle handlers */<br>
>> >> > +<br>
>> >> > +static gboolean tree_item_toggle_get_val(GtkTr<wbr>eeStore *tree_store, gchar *path_str, GtkTreeIter *iter, enum column_id col_id)<br>
>> >> > +{<br>
>> >> > +    gboolean toggle_val;<br>
>> >> > +<br>
>> >> > +    gtk_tree_model_get_iter_from_s<wbr>tring(GTK_TREE_MODEL(tree_stor<wbr>e), iter, path_str);<br>
>> >> > +    gtk_tree_model_get(GTK_TREE_MO<wbr>DEL(tree_store), iter, col_id, &toggle_val, -1);<br>
>> >> > +<br>
>> >> > +    return toggle_val;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void tree_item_toggle_set(GtkTreeSt<wbr>ore *tree_store, GtkTreeIter *iter, enum column_id col_id, gboolean new_val)<br>
>> >> > +{<br>
>> >> > +    gtk_tree_store_set(tree_store, iter, col_id, new_val, -1);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +typedef struct _connect_cb_data {<br>
>> >> > +    SpiceUsbDeviceWidget *self;<br>
>> >> > +    SpiceUsbDevice *usb_dev;<br>
>> >> > +} connect_cb_data;<br>
>> >> > +<br>
>> >> > +static void connect_cb_data_free(connect_c<wbr>b_data *user_data)<br>
>> >> > +{<br>
>> >> > +    spice_usb_device_widget_update<wbr>_status(user_data->self);<br>
>> >> > +    g_object_unref(user_data->self<wbr>);<br>
>> >> > +    g_boxed_free(spice_usb_device_<wbr>get_type(), user_data->usb_dev);<br>
>> >> > +    g_free(user_data);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void usb_widget_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    connect_cb_data *cb_data = user_data;<br>
>> >> > +    SpiceUsbDeviceWidget *self = cb_data->self;<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    SpiceUsbDevice *usb_dev = cb_data->usb_dev;<br>
>> >> > +    GError *err = NULL;<br>
>> >> > +    GtkTreeIter *dev_iter;<br>
>> >> > +    gchar *desc;<br>
>> >> > +    gboolean finished;<br>
>> >> > +    gboolean is_cd = spice_usb_device_manager_is_de<wbr>vice_cd(priv->manager, usb_dev);<br>
>> >> > +    GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : priv->usb_tree.tree_store;<br>
>> >> > +<br>
>> >> > +    dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_dev);<br>
>> >> > +    if (!dev_iter) {<br>
>> >> > +        return;<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    desc = usb_device_description(priv->m<wbr>anager, usb_dev, priv->device_format_string);<br>
>> >> > +    SPICE_DEBUG("Connect cb: %p %s", usb_dev, desc);<br>
>> >> > +<br>
>> >> > +    finished = spice_usb_device_manager_conne<wbr>ct_device_finish(priv->manager<wbr>, res, &err);<br>
>> >> > +    if (finished) {<br>
>> >> > +        gtk_tree_store_set(tree_store, dev_iter,<br>
>> >> > +                           COL_CONNECT_ICON, priv->icon_connected,<br>
>> >> > +                           COL_CONNECTED, TRUE,<br>
>> >> > +                           -1);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_tree_store_set(tree_store, dev_iter,<br>
>> >> > +                           COL_REDIRECT, FALSE,<br>
>> >> > +                           -1);<br>
>> >> > +        g_prefix_error(&err, "Device connect failed %s: ", desc);<br>
>> >> > +        if (err) {<br>
>> >> > +            SPICE_DEBUG("%s", err->message);<br>
>> >> > +            g_signal_emit(self, signals[CONNECT_FAILED], 0, usb_dev, err);<br>
>> >> > +            g_error_free(err);<br>
>> >> > +        } else {<br>
>> >> > +            g_signal_emit(self, signals[CONNECT_FAILED], 0, usb_dev, NULL);<br>
>> >> > +        }<br>
>> >> > +<br>
>> >> > +        /* don't trigger a disconnect if connect failed */<br>
>> >> > +        /*<br>
>> >> > +        g_signal_handlers_block_by_fun<wbr>c(GTK_TOGGLE_BUTTON(user_data-<wbr>>check),<br>
>> >> > +                                        checkbox_clicked_cb, self);<br>
>> >> > +        gtk_toggle_button_set_active(G<wbr>TK_TOGGLE_BUTTON(user_data->ch<wbr>eck), FALSE);<br>
>> >> > +        g_signal_handlers_unblock_by_f<wbr>unc(GTK_TOGGLE_BUTTON(user_dat<wbr>a->check),<br>
>> >> > +                                        checkbox_clicked_cb, self);<br>
>> >> > +        */<br>
>> >> > +    }<br>
>> >> > +    g_free(desc);<br>
>> >> > +    g_free(dev_iter);<br>
>> >> > +    connect_cb_data_free(user_data<wbr>);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void usb_widget_disconnect_cb(GObje<wbr>ct *source_object, GAsyncResult *res, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    connect_cb_data *cb_data = user_data;<br>
>> >> > +    SpiceUsbDeviceWidget *self = cb_data->self;<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    SpiceUsbDevice *usb_dev = cb_data->usb_dev;<br>
>> >> > +    GError *err = NULL;<br>
>> >> > +    GtkTreeIter *dev_iter;<br>
>> >> > +    gchar *desc;<br>
>> >> > +    gboolean finished;<br>
>> >> > +    gboolean is_cd = spice_usb_device_manager_is_de<wbr>vice_cd(priv->manager, usb_dev);<br>
>> >> > +    GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : priv->usb_tree.tree_store;<br>
>> >> > +<br>
>> >> > +    dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_dev);<br>
>> >> > +    if (!dev_iter) {<br>
>> >> > +        return;<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    desc = usb_device_description(priv->m<wbr>anager, usb_dev, priv->device_format_string);<br>
>> >> > +    SPICE_DEBUG("Disconnect cb: %p %s", usb_dev, desc);<br>
>> >> > +<br>
>> >> > +    finished = spice_usb_device_manager_disco<wbr>nnect_device_finish(priv->mana<wbr>ger, res, &err);<br>
>> >> > +    if (finished) {<br>
>> >> > +        gtk_tree_store_set(tree_store, dev_iter,<br>
>> >> > +                           COL_CONNECT_ICON, priv->icon_disconn,<br>
>> >> > +                           COL_CONNECTED, FALSE,<br>
>> >> > +                           -1);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_tree_store_set(tree_store, dev_iter,<br>
>> >> > +                           COL_REDIRECT, TRUE,<br>
>> >> > +                           -1);<br>
>> >> > +        g_prefix_error(&err, "Device disconnect failed %s: ", desc);<br>
>> >> > +        if (err) {<br>
>> >> > +            SPICE_DEBUG("%s", err->message);<br>
>> >> > +            g_error_free(err);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +    g_free(desc);<br>
>> >> > +    g_free(dev_iter);<br>
>> >> > +    connect_cb_data_free(user_data<wbr>);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void tree_item_toggled_cb_redirect(<wbr>GtkCellRendererToggle *cell,<br>
>> >> > +                                          gchar *path_str,<br>
>> >> > +                                          SpiceUsbDeviceWidget *self,<br>
>> >> > +                                          GtkTreeStore *tree_store)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    connect_cb_data *cb_data = g_new(connect_cb_data, 1);<br>
>> >> > +    SpiceUsbDevice *usb_dev;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +    gboolean new_redirect_val;<br>
>> >> > +<br>
>> >> > +    new_redirect_val = !tree_item_toggle_get_val(tree<wbr>_store, path_str, &iter, COL_REDIRECT);<br>
>> >> > +    SPICE_DEBUG("Redirect: %s", new_redirect_val ? "ON" : "OFF");<br>
>> >> > +    tree_item_toggle_set(tree_stor<wbr>e, &iter, COL_REDIRECT, new_redirect_val);<br>
>> >> > +<br>
>> >> > +    gtk_tree_model_get(GTK_TREE_MO<wbr>DEL(tree_store), &iter, COL_ITEM_DATA, (gpointer *)&usb_dev, -1);<br>
>> >> > +    cb_data->self = g_object_ref(self);<br>
>> >> > +    cb_data->usb_dev = g_boxed_copy(spice_usb_device_<wbr>get_type(), usb_dev);<br>
>> >> > +<br>
>> >> > +    if (new_redirect_val) {<br>
>> >> > +        spice_usb_device_manager_conne<wbr>ct_device_async(priv->manager, usb_dev,<br>
>> >> > +                                                      NULL, /* cancellable */<br>
>> >> > +                                                      usb_widget_connect_cb, cb_data);<br>
>> >> > +    } else {<br>
>> >> > +        spice_usb_device_manager_disco<wbr>nnect_device_async(priv->manag<wbr>er, usb_dev,<br>
>> >> > +                                                         NULL, /* cancellable */<br>
>> >> > +                                                         usb_widget_disconnect_cb, cb_data);<br>
>> >> > +<br>
>> >> > +    }<br>
>> >> > +    spice_usb_device_widget_update<wbr>_status(self);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void tree_item_toggled_cb_redirect_<wbr>cd(GtkCellRendererToggle *cell,<br>
>> >> > +                                             gchar *path_str,<br>
>> >> > +                                             gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    tree_item_toggled_cb_redirect(<wbr>cell, path_str, self, priv->cd_tree.tree_store);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void tree_item_toggled_cb_redirect_<wbr>usb(GtkCellRendererToggle *cell,<br>
>> >> > +                                             gchar *path_str,<br>
>> >> > +                                             gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    tree_item_toggled_cb_redirect(<wbr>cell, path_str, self, priv->usb_tree.tree_store);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +/* Signal handlers */<br>
>> >> > +<br>
>> >> > +static void device_added_cb(SpiceUsbDevice<wbr>Manager *usb_dev_mgr,<br>
>> >> > +    SpiceUsbDevice *usb_device, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("Signal: Device Added");<br>
>> >> > +<br>
>> >> > +    usb_widget_add_device(self, usb_device, NULL);<br>
>> >> > +<br>
>> >> > +    spice_usb_device_widget_update<wbr>_status(self);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void device_removed_cb(SpiceUsbDevi<wbr>ceManager *usb_dev_mgr,<br>
>> >> > +    SpiceUsbDevice *usb_device, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    gboolean dev_removed;<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("Signal: Device Removed");<br>
>> >> > +<br>
>> >> > +    dev_removed = usb_widget_remove_device(self, usb_device);<br>
>> >> > +    if (dev_removed) {<br>
>> >> > +        spice_usb_device_widget_update<wbr>_status(self);<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void device_changed_cb(SpiceUsbDevi<wbr>ceManager *usb_dev_mgr,<br>
>> >> > +    SpiceUsbDevice *usb_device, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeIter *old_dev_iter;<br>
>> >> > +    gboolean is_cd = spice_usb_device_manager_is_de<wbr>vice_cd(priv->manager, usb_device);<br>
>> >> > +    GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : priv->usb_tree.tree_store;<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("Signal: Device Changed");<br>
>> >> > +<br>
>> >> > +    old_dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_device);<br>
>> >> > +    if (old_dev_iter != NULL) {<br>
>> >> > +<br>
>> >> > +        usb_widget_add_device(self, usb_device, old_dev_iter);<br>
>> >> > +<br>
>> >> > +        spice_usb_device_widget_update<wbr>_status(self);<br>
>> >> > +        g_free(old_dev_iter);<br>
>> >> > +    } else {<br>
>> >> > +        SPICE_DEBUG("Device not found!");<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void device_error_cb(SpiceUsbDevice<wbr>Manager *manager,<br>
>> >> > +    SpiceUsbDevice *usb_device, GError *err, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeIter *dev_iter;<br>
>> >> > +    gboolean is_cd = spice_usb_device_manager_is_de<wbr>vice_cd(priv->manager, usb_device);<br>
>> >> > +    GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : priv->usb_tree.tree_store;<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("Signal: Device Error");<br>
>> >> > +<br>
>> >> > +    dev_iter = usb_widget_tree_store_find_usb<wbr>_device(tree_store, usb_device);<br>
>> >> > +    if (dev_iter != NULL) {<br>
>> >> > +        tree_item_toggle_set(tree_stor<wbr>e, dev_iter, COL_REDIRECT, FALSE);<br>
>> >> > +        spice_usb_device_widget_update<wbr>_status(self);<br>
>> >> > +        g_free(dev_iter);<br>
>> >> > +    } else {<br>
>> >> > +        SPICE_DEBUG("Device not found!");<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +/* Selection handler */<br>
>> >> > +<br>
>> >> > +static void tree_selection_changed_cb(GtkT<wbr>reeSelection *select, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +    GtkTreePath *path;<br>
>> >> > +    gboolean is_lun;<br>
>> >> > +    UsbWidgetLunItem *lun_item;<br>
>> >> > +    gchar *txt[3];<br>
>> >> > +<br>
>> >> > +    if (gtk_tree_selection_get_select<wbr>ed(select, &tree_model, &iter)) {<br>
>> >> > +        gtk_tree_model_get(tree_model, &iter,<br>
>> >> > +                COL_VENDOR, &txt[0],<br>
>> >> > +                COL_PRODUCT, &txt[1],<br>
>> >> > +                COL_REVISION, &txt[2],<br>
>> >> > +                COL_LUN_ITEM, &is_lun,<br>
>> >> > +                COL_ITEM_DATA, (gpointer *)&lun_item,<br>
>> >> > +                -1);<br>
>> >> > +        path = gtk_tree_model_get_path(tree_m<wbr>odel, &iter);<br>
>> >> > +<br>
>> >> > +        SPICE_DEBUG("selected: %s,%s,%s [%s %s] [%s]",<br>
>> >> > +                txt[0], txt[1],<br>
>> >> > +                is_lun ? txt[2] : "--",<br>
>> >> > +                is_lun ? "LUN" : "USB-DEV",<br>
>> >> > +                is_lun ? lun_item->info.file_path : "--",<br>
>> >> > +                gtk_tree_path_to_string(path))<wbr>;<br>
>> >> > +<br>
>> >> > +        if (txt[0]) {<br>
>> >> > +            g_free(txt[0]);<br>
>> >> > +        }<br>
>> >> > +        if (txt[1]) {<br>
>> >> > +            g_free(txt[1]);<br>
>> >> > +        }<br>
>> >> > +        if (txt[2]) {<br>
>> >> > +            g_free(txt[2]);<br>
>> >> > +        }<br>
>> >> > +        gtk_tree_path_free(path);<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkTreeSelection* set_selection_handler(GtkTreeV<wbr>iew *tree_view)<br>
>> >> > +{<br>
>> >> > +    GtkTreeSelection *select;<br>
>> >> > +<br>
>> >> > +    select = gtk_tree_view_get_selection(tr<wbr>ee_view);<br>
>> >> > +    gtk_tree_selection_set_mode(se<wbr>lect, GTK_SELECTION_SINGLE);<br>
>> >> > +<br>
>> >> > +    g_signal_connect(G_OBJECT(sele<wbr>ct), "changed",<br>
>> >> > +                     G_CALLBACK(tree_selection_cha<wbr>nged_cb),<br>
>> >> > +                     NULL);<br>
>> >> > +<br>
>> >> > +    SPICE_DEBUG("selection handler set");<br>
>> >> > +    return select;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkWidget *create_image_button_box(const gchar *label_str, const gchar *icon_name, GtkWidget *parent)<br>
>> >> > +{<br>
>> >> > +    GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HO<wbr>RIZONTAL, 6);<br>
>> >> > +    GtkWidget *icon = gtk_image_new_from_icon_name(i<wbr>con_name, GTK_ICON_SIZE_MENU);<br>
>> >> > +    GtkWidget *label = gtk_accel_label_new(label_str)<wbr>;<br>
>> >> > +    GtkAccelGroup *accel_group = gtk_accel_group_new();<br>
>> >> > +    guint accel_key;<br>
>> >> > +<br>
>> >> > +    /* add icon */<br>
>> >> > +    gtk_container_add(GTK_CONTAINE<wbr>R(box), icon);<br>
>> >> > +<br>
>> >> > +    /* add label */<br>
>> >> > +    gtk_label_set_xalign(GTK_LABEL<wbr>(label), 0.0);<br>
>> >> > +    gtk_label_set_use_underline(GT<wbr>K_LABEL(label), TRUE);<br>
>> >> > +    g_object_get(G_OBJECT(label), "mnemonic-keyval", &accel_key, NULL);<br>
>> >> > +    gtk_widget_add_accelerator(par<wbr>ent, "activate", accel_group, accel_key,<br>
>> >> > +                               GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);<br>
>> >> > +    gtk_accel_label_set_accel_widg<wbr>et(GTK_ACCEL_LABEL(label), parent);<br>
>> >> > +    gtk_box_pack_end(GTK_BOX(box), label, TRUE, TRUE, 0);<br>
>> >> > +<br>
>> >> > +    /* add the new box to the parent widget */<br>
>> >> > +    gtk_container_add(GTK_CONTAINE<wbr>R(parent), box);<br>
>> >> > +<br>
>> >> > +    return box;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +/* LUN properties dialog */<br>
>> >> > +<br>
>> >> > +typedef struct _lun_properties_dialog {<br>
>> >> > +    GtkWidget *dialog;<br>
>> >> > +    GtkWidget *advanced_grid;<br>
>> >> > +    gboolean advanced_shown;<br>
>> >> > +<br>
>> >> > +    GtkWidget *file_entry;<br>
>> >> > +    GtkWidget *vendor_entry;<br>
>> >> > +    GtkWidget *product_entry;<br>
>> >> > +    GtkWidget *revision_entry;<br>
>> >> > +    GtkWidget *loaded_switch;<br>
>> >> > +    GtkWidget *locked_switch;<br>
>> >> > +} lun_properties_dialog;<br>
>> >> > +<br>
>> >> > +#if 1<br>
>> >> > +static void usb_cd_choose_file(GtkWidget *button, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    GtkWidget *file_entry = (GtkWidget *)user_data;<br>
>> >> > +    GtkFileChooserNative *native;<br>
>> >> > +    GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;<br>
>> >> > +    gint res;<br>
>> >> > +<br>
>> >> > +    native = gtk_file_chooser_native_new("C<wbr>hoose File for USB CD",<br>
>> >> > +        GTK_WINDOW(gtk_widget_get_topl<wbr>evel(file_entry)),<br>
>> >> > +        action,<br>
>> >> > +        "_Open",<br>
>> >> > +        "_Cancel");<br>
>> >> > +<br>
>> >> > +    res = gtk_native_dialog_run(GTK_NATI<wbr>VE_DIALOG(native));<br>
>> >> > +    if (res == GTK_RESPONSE_ACCEPT) {<br>
>> >> > +        char *filename = gtk_file_chooser_get_filename(<wbr>GTK_FILE_CHOOSER(native));<br>
>> >> > +        gtk_entry_set_alignment(GTK_EN<wbr>TRY(file_entry), 1);<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(f<wbr>ile_entry), filename);<br>
>> >> > +        g_free(filename);<br>
>> >> > +    }<br>
>> >> > +    else {<br>
>> >> > +        gtk_widget_grab_focus(button);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    g_object_unref(native);<br>
>> >> > +}<br>
>> >> > +#else<br>
>> >> > +// to be removed<br>
>> >> > +static void usb_cd_choose_file(GtkWidget *button, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    GtkWidget *file_entry = (GtkWidget *)user_data;<br>
>> >> > +    GtkWidget *dialog;<br>
>> >> > +    gint res;<br>
>> >> > +<br>
>> >> > +    dialog = gtk_file_chooser_dialog_new ("Choose File for USB CD",<br>
>> >> > +                                          GTK_WINDOW(gtk_widget_get_topl<wbr>evel(file_entry)),<br>
>> >> > +                                          GTK_FILE_CHOOSER_ACTION_OPEN,<br>
>> >> > +                                          "_Cancel",<br>
>> >> > +                                          GTK_RESPONSE_CANCEL,<br>
>> >> > +                                          "_Ok",<br>
>> >> > +                                          GTK_RESPONSE_ACCEPT,<br>
>> >> > +                                          NULL);<br>
>> >> > +    gtk_dialog_set_default_respons<wbr>e(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);<br>
>> >> > +<br>
>> >> > +    res = gtk_dialog_run(GTK_DIALOG(dial<wbr>og));<br>
>> >> > +    if (res == GTK_RESPONSE_ACCEPT) {<br>
>> >> > +        char *filename = gtk_file_chooser_get_filename(<wbr>GTK_FILE_CHOOSER(dialog));<br>
>> >> > +        gtk_entry_set_alignment(GTK_EN<wbr>TRY(file_entry), 1);<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(f<wbr>ile_entry), filename);<br>
>> >> > +        g_free(filename);<br>
>> >> > +    }<br>
>> >> > +    gtk_widget_destroy(dialog);<br>
>> >> > +}<br>
>> >> > +#endif<br>
>> >> > +<br>
>> >> > +static gboolean lun_properties_dialog_loaded_s<wbr>witch_cb(GtkWidget *widget,<br>
>> >> > +                                                       gboolean state, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    lun_properties_dialog *lun_dialog = user_data;<br>
>> >> > +<br>
>> >> > +    gtk_widget_set_sensitive(lun_d<wbr>ialog->locked_switch, state);<br>
>> >> > +    gtk_widget_set_can_focus(lun_d<wbr>ialog->locked_switch, state);<br>
>> >> > +<br>
>> >> > +    return FALSE; /* call default signal handler */<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void lun_properties_dialog_toggle_a<wbr>dvanced(GtkWidget *widget, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    lun_properties_dialog *lun_dialog = user_data;<br>
>> >> > +<br>
>> >> > +    if (lun_dialog->advanced_shown) {<br>
>> >> > +        gtk_widget_hide(lun_dialog->ad<wbr>vanced_grid);<br>
>> >> > +        lun_dialog->advanced_shown = FALSE;<br>
>> >> > +    } else {<br>
>> >> > +        gtk_widget_show_all(lun_dialog<wbr>->advanced_grid);<br>
>> >> > +        lun_dialog->advanced_shown = TRUE;<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void create_lun_properties_dialog(S<wbr>piceUsbDeviceWidget *self,<br>
>> >> > +                                         GtkWidget *parent_window,<br>
>> >> > +                                         SpiceUsbDeviceLunInfo *lun_info,<br>
>> >> > +                                         lun_properties_dialog *lun_dialog)<br>
>> >> > +{<br>
>> >> > +    // SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkWidget *dialog, *content_area;<br>
>> >> > +    GtkWidget *grid, *advanced_grid;<br>
>> >> > +    GtkWidget *file_entry, *choose_button;<br>
>> >> > +    GtkWidget *advanced_button, *advanced_icon;<br>
>> >> > +    GtkWidget *vendor_entry, *product_entry, *revision_entry;<br>
>> >> > +    GtkWidget *loaded_switch, *loaded_label;<br>
>> >> > +    GtkWidget *locked_switch, *locked_label;<br>
>> >> > +    gint nrow = 0;<br>
>> >> > +<br>
>> >> > +    dialog = gtk_dialog_new_with_buttons (!lun_info ? "Add CD LUN" : "CD LUN Settings",<br>
>> >> > +                    GTK_WINDOW(parent_window),<br>
>> >> > +                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT<wbr>, /* flags */<br>
>> >> > +                    !lun_info ? "Add" : "OK", GTK_RESPONSE_ACCEPT,<br>
>> >> > +                    "Cancel", GTK_RESPONSE_REJECT,<br>
>> >> > +                    NULL);<br>
>> >> > +<br>
>> >> > +    gtk_dialog_set_default_respons<wbr>e(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);<br>
>> >> > +    gtk_container_set_border_width<wbr>(GTK_CONTAINER(dialog), 12);<br>
>> >> > +    gtk_box_set_spacing(GTK_BOX(gt<wbr>k_bin_get_child(GTK_BIN(dialog<wbr>))), 12);<br>
>> >> > +<br>
>> >> > +    content_area = gtk_dialog_get_content_area(GT<wbr>K_DIALOG(dialog));<br>
>> >> > +<br>
>> >> > +    /* main grid - always visible */<br>
>> >> > +    grid = gtk_grid_new();<br>
>> >> > +    gtk_grid_set_row_spacing(GTK_G<wbr>RID(grid), 12);<br>
>> >> > +    gtk_grid_set_column_homogeneou<wbr>s(GTK_GRID(grid), FALSE);<br>
>> >> > +    gtk_container_add(GTK_CONTAINE<wbr>R(content_area), grid);<br>
>> >> > +<br>
>> >> > +    /* File path label */<br>
>> >> > +    gtk_grid_attach(GTK_GRID(grid)<wbr>,<br>
>> >> > +            gtk_label_new("Select file or device"),<br>
>> >> > +            0, nrow++, // left top<br>
>> >> > +            7, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* file/device path entry */<br>
>> >> > +    file_entry = gtk_entry_new();<br>
>> >> > +    gtk_widget_set_hexpand(file_en<wbr>try, TRUE);<br>
>> >> > +    if (!lun_info) {<br>
>> >> > +        gtk_entry_set_placeholder_text<wbr>(GTK_ENTRY(file_entry), "file-path");<br>
>> >> > +    } else {<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(f<wbr>ile_entry), lun_info->file_path);<br>
>> >> > +        if (lun_info->loaded) {<br>
>> >> > +            gtk_editable_set_editable(GTK_<wbr>EDITABLE(file_entry), FALSE);<br>
>> >> > +            gtk_widget_set_can_focus(file_<wbr>entry, FALSE);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +    gtk_grid_attach(GTK_GRID(grid)<wbr>,<br>
>> >> > +            file_entry,<br>
>> >> > +            0, nrow, // left top<br>
>> >> > +            6, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* choose button */<br>
>> >> > +    choose_button = gtk_button_new_with_mnemonic("<wbr>_Choose File");<br>
>> >> > +    gtk_widget_set_hexpand(choose_<wbr>button, FALSE);<br>
>> >> > +    g_signal_connect(GTK_BUTTON(ch<wbr>oose_button),<br>
>> >> > +                     "clicked", G_CALLBACK(usb_cd_choose_file)<wbr>, file_entry);<br>
>> >> > +    if (lun_info && lun_info->loaded) {<br>
>> >> > +        gtk_widget_set_sensitive(choos<wbr>e_button, FALSE);<br>
>> >> > +        gtk_widget_set_can_focus(choos<wbr>e_button, FALSE);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    gtk_grid_attach(GTK_GRID(grid)<wbr>,<br>
>> >> > +            choose_button,<br>
>> >> > +            6, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* advanced button */<br>
>> >> > +    advanced_button = gtk_button_new_with_label("Adv<wbr>anced");<br>
>> >> > +    gtk_button_set_relief(GTK_BUTT<wbr>ON(advanced_button), GTK_RELIEF_NONE);<br>
>> >> > +    advanced_icon = gtk_image_new_from_icon_name("<wbr>preferences-system", GTK_ICON_SIZE_BUTTON);<br>
>> >> > +    gtk_button_set_image(GTK_BUTTO<wbr>N(advanced_button), advanced_icon);<br>
>> >> > +    gtk_button_set_always_show_ima<wbr>ge(GTK_BUTTON(advanced_button)<wbr>, TRUE);<br>
>> >> > +    g_signal_connect(advanced_butt<wbr>on, "clicked", G_CALLBACK(lun_properties_dial<wbr>og_toggle_advanced), lun_dialog);<br>
>> >> > +<br>
>> >> > +    gtk_grid_attach(GTK_GRID(grid)<wbr>,<br>
>> >> > +            advanced_button,<br>
>> >> > +            0, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* advanced grid */<br>
>> >> > +    advanced_grid = gtk_grid_new();<br>
>> >> > +    gtk_grid_set_row_spacing(GTK_G<wbr>RID(advanced_grid), 12);<br>
>> >> > +    gtk_grid_set_column_homogeneou<wbr>s(GTK_GRID(advanced_grid), FALSE);<br>
>> >> > +    gtk_container_add(GTK_CONTAINE<wbr>R(content_area), advanced_grid);<br>
>> >> > +<br>
>> >> > +    /* horizontal separator */<br>
>> >> > +    gtk_container_add(GTK_CONTAINE<wbr>R(content_area), gtk_separator_new(GTK_ORIENTAT<wbr>ION_HORIZONTAL));<br>
>> >> > +<br>
>> >> > +    /* pack advanced grid */<br>
>> >> > +    nrow = 0;<br>
>> >> > +<br>
>> >> > +    /* horizontal separator */<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            gtk_separator_new(GTK_ORIENTAT<wbr>ION_HORIZONTAL),<br>
>> >> > +            0, nrow++, // left top<br>
>> >> > +            7, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* product id labels */<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            gtk_label_new("Vendor"),<br>
>> >> > +            0, nrow, // left top<br>
>> >> > +            2, 1); // width height<br>
>> >> > +<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            gtk_label_new("Product"),<br>
>> >> > +            2, nrow, // left top<br>
>> >> > +            4, 1); // width height<br>
>> >> > +<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            gtk_label_new("Revision"),<br>
>> >> > +            6, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* vendor entry */<br>
>> >> > +    vendor_entry = gtk_entry_new();<br>
>> >> > +    gtk_entry_set_max_length(GTK_E<wbr>NTRY(vendor_entry), 8);<br>
>> >> > +    if (lun_info) {<br>
>> >> > +        gtk_widget_set_sensitive(vendo<wbr>r_entry, FALSE);<br>
>> >> > +        gtk_widget_set_can_focus(vendo<wbr>r_entry, FALSE);<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(v<wbr>endor_entry), lun_info->vendor);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_entry_set_placeholder_text<wbr>(GTK_ENTRY(vendor_entry), "auto");<br>
>> >> > +    }<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            vendor_entry,<br>
>> >> > +            0, nrow, // left top<br>
>> >> > +            2, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* product entry */<br>
>> >> > +    product_entry = gtk_entry_new();<br>
>> >> > +    gtk_entry_set_max_length(GTK_E<wbr>NTRY(product_entry), 16);<br>
>> >> > +    if (lun_info) {<br>
>> >> > +        gtk_widget_set_sensitive(produ<wbr>ct_entry, FALSE);<br>
>> >> > +        gtk_widget_set_can_focus(produ<wbr>ct_entry, FALSE);<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(p<wbr>roduct_entry), lun_info->product);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_entry_set_placeholder_text<wbr>(GTK_ENTRY(product_entry), "auto");<br>
>> >> > +    }<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            product_entry,<br>
>> >> > +            2, nrow, // left top<br>
>> >> > +            4, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* revision entry */<br>
>> >> > +    revision_entry = gtk_entry_new();<br>
>> >> > +    gtk_entry_set_max_length(GTK_E<wbr>NTRY(revision_entry), 4);<br>
>> >> > +    if (lun_info) {<br>
>> >> > +        gtk_widget_set_sensitive(revis<wbr>ion_entry, FALSE);<br>
>> >> > +        gtk_widget_set_can_focus(revis<wbr>ion_entry, FALSE);<br>
>> >> > +        gtk_entry_set_text(GTK_ENTRY(r<wbr>evision_entry), lun_info->revision);<br>
>> >> > +    } else {<br>
>> >> > +        gtk_entry_set_placeholder_text<wbr>(GTK_ENTRY(revision_entry), "auto");<br>
>> >> > +    }<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            revision_entry,<br>
>> >> > +            6, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* horizontal separator */<br>
>> >> > +    if (!lun_info) {<br>
>> >> > +        gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +                gtk_separator_new(GTK_ORIENTAT<wbr>ION_HORIZONTAL),<br>
>> >> > +                0, nrow++, // left top<br>
>> >> > +                7, 1); // width height<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    /* initially loaded switch */<br>
>> >> > +    loaded_label = gtk_label_new("Initially loaded:");<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +        loaded_label,<br>
>> >> > +        0, nrow, // left top<br>
>> >> > +        2, 1); // width height<br>
>> >> > +<br>
>> >> > +    loaded_switch = gtk_switch_new();<br>
>> >> > +    gtk_switch_set_state(GTK_SWITC<wbr>H(loaded_switch), TRUE);<br>
>> >> > +    if (lun_info) {<br>
>> >> > +        gtk_widget_set_child_visible(l<wbr>oaded_switch, FALSE);<br>
>> >> > +        gtk_widget_set_child_visible(l<wbr>oaded_label, FALSE);<br>
>> >> > +    } else {<br>
>> >> > +        g_signal_connect(loaded_switch<wbr>, "state-set",<br>
>> >> > +                         G_CALLBACK(lun_properties_dia<wbr>log_loaded_switch_cb), lun_dialog);<br>
>> >> > +    }<br>
>> >> > +    gtk_widget_set_halign(loaded_s<wbr>witch, GTK_ALIGN_START);<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            loaded_switch,<br>
>> >> > +            2, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    /* initially locked switch */<br>
>> >> > +    locked_label = gtk_label_new("Initially locked:");<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +        locked_label,<br>
>> >> > +        0, nrow, // left top<br>
>> >> > +        2, 1); // width height<br>
>> >> > +<br>
>> >> > +    locked_switch = gtk_switch_new();<br>
>> >> > +    gtk_switch_set_state(GTK_SWITC<wbr>H(locked_switch), FALSE);<br>
>> >> > +    gtk_widget_set_hexpand(locked_<wbr>switch, FALSE);<br>
>> >> > +    if (lun_info) {<br>
>> >> > +        gtk_widget_set_child_visible(l<wbr>ocked_switch, FALSE);<br>
>> >> > +        gtk_widget_set_child_visible(l<wbr>ocked_label, FALSE);<br>
>> >> > +    }<br>
>> >> > +    gtk_widget_set_halign(locked_s<wbr>witch, GTK_ALIGN_START);<br>
>> >> > +    gtk_grid_attach(GTK_GRID(advan<wbr>ced_grid),<br>
>> >> > +            locked_switch,<br>
>> >> > +            2, nrow++, // left top<br>
>> >> > +            1, 1); // width height<br>
>> >> > +<br>
>> >> > +    lun_dialog->dialog = dialog;<br>
>> >> > +    lun_dialog->advanced_grid = advanced_grid;<br>
>> >> > +    lun_dialog->advanced_shown = FALSE;<br>
>> >> > +    lun_dialog->file_entry = file_entry;<br>
>> >> > +    lun_dialog->vendor_entry = vendor_entry;<br>
>> >> > +    lun_dialog->product_entry = product_entry;<br>
>> >> > +    lun_dialog->revision_entry = revision_entry;<br>
>> >> > +    lun_dialog->loaded_switch = loaded_switch;<br>
>> >> > +    lun_dialog->locked_switch = locked_switch;<br>
>> >> > +<br>
>> >> > +    gtk_widget_show_all(dialog);<br>
>> >> > +    gtk_widget_hide(advanced_grid)<wbr>;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void lun_properties_dialog_get_info<wbr>(lun_properties_dialog *lun_dialog,<br>
>> >> > +                                            SpiceUsbDeviceLunInfo *lun_info)<br>
>> >> > +{<br>
>> >> > +    lun_info->file_path = gtk_entry_get_text(GTK_ENTRY(l<wbr>un_dialog->file_entry));<br>
>> >> > +    lun_info->vendor = gtk_entry_get_text(GTK_ENTRY(l<wbr>un_dialog->vendor_entry));<br>
>> >> > +    lun_info->product = gtk_entry_get_text(GTK_ENTRY(l<wbr>un_dialog->product_entry));<br>
>> >> > +    lun_info->revision = gtk_entry_get_text(GTK_ENTRY(l<wbr>un_dialog->revision_entry));<br>
>> >> > +    lun_info->loaded = gtk_switch_get_active(GTK_SWIT<wbr>CH(lun_dialog->loaded_switch))<wbr>;<br>
>> >> > +    lun_info->locked = gtk_switch_get_active(GTK_SWIT<wbr>CH(lun_dialog->locked_switch))<wbr>;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +/* Popup menu */<br>
>> >> > +static void view_popup_menu_on_eject(GtkWi<wbr>dget *menuitem, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeSelection *select = gtk_tree_view_get_selection(pr<wbr>iv->cd_tree.tree_view);<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +<br>
>> >> > +    if (gtk_tree_selection_get_select<wbr>ed(select, &tree_model, &iter)) {<br>
>> >> > +        if (!tree_item_is_lun(priv->cd_tr<wbr>ee.tree_store, &iter)) {<br>
>> >> > +            SpiceUsbDevice *usb_device;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&usb_device, -1);<br>
>> >> > +            SPICE_DEBUG("%s - not applicable for USB device", __FUNCTION__);<br>
>> >> > +        }<br>
>> >> > +        else {<br>
>> >> > +            UsbWidgetLunItem *lun_item;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&lun_item, -1);<br>
>> >> > +            spice_usb_device_manager_devic<wbr>e_lun_load(<br>
>> >> > +                lun_item->manager, lun_item->device, lun_item->lun, !lun_item->info.loaded);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +    else {<br>
>> >> > +        SPICE_DEBUG("%s - failed to get selection", __FUNCTION__);<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void view_popup_menu_on_lock(GtkWid<wbr>get *menuitem, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeSelection *select = gtk_tree_view_get_selection(pr<wbr>iv->cd_tree.tree_view);<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +<br>
>> >> > +    if (gtk_tree_selection_get_select<wbr>ed(select, &tree_model, &iter)) {<br>
>> >> > +        if (!tree_item_is_lun(priv->cd_tr<wbr>ee.tree_store, &iter)) {<br>
>> >> > +            SpiceUsbDevice *usb_device;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&usb_device, -1);<br>
>> >> > +            SPICE_DEBUG("%s - not applicable for USB device", __FUNCTION__);<br>
>> >> > +        }<br>
>> >> > +        else {<br>
>> >> > +            UsbWidgetLunItem *lun_item;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&lun_item, -1);<br>
>> >> > +            spice_usb_device_manager_devic<wbr>e_lun_lock(<br>
>> >> > +                lun_item->manager, lun_item->device, lun_item->lun, !lun_item->info.locked);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +    else {<br>
>> >> > +        SPICE_DEBUG("%s - failed to get selection", __FUNCTION__);<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void view_popup_menu_on_remove(GtkW<wbr>idget *menuitem, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeSelection *select = gtk_tree_view_get_selection(pr<wbr>iv->cd_tree.tree_view);<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +<br>
>> >> > +    if (gtk_tree_selection_get_select<wbr>ed(select, &tree_model, &iter)) {<br>
>> >> > +        if (!tree_item_is_lun(priv->cd_tr<wbr>ee.tree_store, &iter)) {<br>
>> >> > +            SpiceUsbDevice *usb_device;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&usb_device, -1);<br>
>> >> > +            SPICE_DEBUG("Remove USB device");<br>
>> >> > +        } else {<br>
>> >> > +            UsbWidgetLunItem *lun_item;<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&lun_item, -1);<br>
>> >> > +            gtk_tree_selection_unselect_al<wbr>l(select);<br>
>> >> > +            spice_usb_device_manager_devic<wbr>e_lun_remove(lun_item->manager<wbr>, lun_item->device, lun_item->lun);<br>
>> >> > +        }<br>
>> >> > +    } else {<br>
>> >> > +        SPICE_DEBUG("Remove - failed to get selection");<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void view_popup_menu_on_settings(Gt<wbr>kWidget *menuitem, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeSelection *select = gtk_tree_view_get_selection(pr<wbr>iv->cd_tree.tree_view);<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +<br>
>> >> > +    if (gtk_tree_selection_get_select<wbr>ed(select, &tree_model, &iter)) {<br>
>> >> > +        if (!tree_item_is_lun(priv->cd_tr<wbr>ee.tree_store, &iter)) {<br>
>> >> > +            SPICE_DEBUG("No settings for USB device yet");<br>
>> >> > +        } else {<br>
>> >> > +            lun_properties_dialog lun_dialog;<br>
>> >> > +            UsbWidgetLunItem *lun_item;<br>
>> >> > +            gint resp;<br>
>> >> > +<br>
>> >> > +            gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, (gpointer *)&lun_item, -1);<br>
>> >> > +            gtk_tree_selection_unselect_al<wbr>l(select);<br>
>> >> > +            create_lun_properties_dialog(s<wbr>elf, NULL, &lun_item->info, &lun_dialog);<br>
>> >> > +<br>
>> >> > +            resp = gtk_dialog_run(GTK_DIALOG(lun_<wbr>dialog.dialog));<br>
>> >> > +            if (resp == GTK_RESPONSE_ACCEPT) {<br>
>> >> > +                SpiceUsbDeviceLunInfo lun_info;<br>
>> >> > +                SPICE_DEBUG("response is ACCEPT");<br>
>> >> > +                lun_properties_dialog_get_info<wbr>(&lun_dialog, &lun_info);<br>
>> >> > +                spice_usb_device_manager_devic<wbr>e_lun_change_media(<br>
>> >> > +                    priv->manager, lun_item->device, lun_item->lun, &lun_info);<br>
>> >> > +            } else {<br>
>> >> > +                SPICE_DEBUG("response is REJECT");<br>
>> >> > +            }<br>
>> >> > +            gtk_widget_destroy(lun_dialog.<wbr>dialog);<br>
>> >> > +        }<br>
>> >> > +    } else {<br>
>> >> > +        SPICE_DEBUG("Remove - failed to get selection");<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static GtkWidget *view_popup_add_menu_item(GtkW<wbr>idget *menu,<br>
>> >> > +    const gchar *label_str,<br>
>> >> > +    const gchar *icon_name,<br>
>> >> > +    GCallback cb_func, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    GtkWidget *menu_item = gtk_menu_item_new();<br>
>> >> > +    create_image_button_box(label_<wbr>str, icon_name, menu_item);<br>
>> >> > +    g_signal_connect(menu_item, "activate", cb_func, user_data);<br>
>> >> > +<br>
>> >> > +    gtk_widget_show_all(menu_item)<wbr>;<br>
>> >> > +    gtk_menu_shell_append(GTK_MENU<wbr>_SHELL(menu), menu_item);<br>
>> >> > +<br>
>> >> > +    return menu_item;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static gboolean has_single_lun(SpiceUsbDeviceW<wbr>idgetPrivate *priv, SpiceUsbDevice *device)<br>
>> >> > +{<br>
>> >> > +    gboolean result;<br>
>> >> > +    SpiceUsbDeviceManager *usb_dev_mgr = priv->manager;<br>
>> >> > +    GArray *lun_array;<br>
>> >> > +    lun_array = spice_usb_device_manager_get_d<wbr>evice_luns(usb_dev_mgr, device);<br>
>> >> > +    result = lun_array && lun_array->len <= 1;<br>
>> >> > +    if (lun_array) {<br>
>> >> > +        g_array_unref(lun_array);<br>
>> >> > +    }<br>
>> >> > +    return result;<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void view_popup_menu(GtkTreeView *tree_view, GdkEventButton *event, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
>> >> > +    GtkTreeSelection *select;<br>
>> >> > +    GtkTreeModel *tree_model;<br>
>> >> > +    GtkTreeIter iter;<br>
>> >> > +    UsbWidgetLunItem *lun_item;<br>
>> >> > +    gboolean is_loaded, is_locked;<br>
>> >> > +    GtkTreeIter *usb_dev_iter;<br>
>> >> > +    gboolean is_dev_connected;<br>
>> >> > +    GtkWidget *menu;<br>
>> >> > +<br>
>> >> > +    if (tree_view != priv->cd_tree.tree_view) {<br>
>> >> > +        SPICE_DEBUG("Not applicable for USB device");<br>
>> >> > +        return;<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    select = gtk_tree_view_get_selection(tr<wbr>ee_view);<br>
>> >> > +<br>
>> >> > +    if (!gtk_tree_selection_get_selec<wbr>ted(select, &tree_model, &iter)) {<br>
>> >> > +        SPICE_DEBUG("No tree view row is selected");<br>
>> >> > +        return;<br>
>> >> > +    }<br>
>> >> > +    if (!tree_item_is_lun(priv->cd_tr<wbr>ee.tree_store, &iter)) {<br>
>> >> > +        SPICE_DEBUG("No settings for USB device yet");<br>
>> >> > +        return;<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    gtk_tree_model_get(tree_model, &iter,<br>
>> >> > +                       COL_ITEM_DATA, (gpointer *)&lun_item,<br>
>> >> > +                       COL_LOADED, &is_loaded,<br>
>> >> > +                       COL_LOCKED, &is_locked,<br>
>> >> > +                       -1);<br>
>> >> > +<br>
>> >> > +    usb_dev_iter = usb_widget_tree_store_find_usb<wbr>_device(priv->cd_tree.tree_sto<wbr>re, lun_item->device);<br>
>> >> > +    if (usb_dev_iter != NULL) {<br>
>> >> > +        gtk_tree_model_get(tree_model, usb_dev_iter,<br>
>> >> > +                           COL_CONNECTED, &is_dev_connected,<br>
>> >> > +                           -1);<br>
>> >> > +        g_free(usb_dev_iter);<br>
>> >> > +    } else {<br>
>> >> > +        is_dev_connected = FALSE;<br>
>> >> > +        SPICE_DEBUG("Failed to find USB device for LUN: %s|%s",<br>
>> >> > +                    lun_item->info.vendor, lun_item->info.product);<br>
>> >> > +    }<br>
>> >> > +    SPICE_DEBUG("Right-click on LUN: %s|%s, dev connected:%d, lun loaded:%d locked:%d",<br>
>> >> > +                lun_item->info.vendor, lun_item->info.product,<br>
>> >> > +                is_dev_connected, is_loaded, is_locked);<br>
>> >> > +<br>
>> >> > +    /* Set up the menu */<br>
>> >> > +    menu = gtk_menu_new();<br>
>> >> > +<br>
>> >> > +    view_popup_add_menu_item(menu, "_Settings", "preferences-system",<br>
>> >> > +                             G_CALLBACK(view_popup_menu_on<wbr>_settings), user_data);<br>
>> >> > +    if (is_loaded) {<br>
>> >> > +        if (!is_locked) {<br>
>> >> > +            view_popup_add_menu_item(menu, "_Lock", "system-lock-screen",<br>
>> >> > +                                    G_CALLBACK(view_popup_menu_on_<wbr>lock), user_data);<br>
>> >> > +            view_popup_add_menu_item(menu, "_Eject", "media-eject",<br>
>> >> > +                                     G_CALLBACK(view_popup_menu_on<wbr>_eject), user_data);<br>
>> >> > +        } else {<br>
>> >> > +            view_popup_add_menu_item(menu, "_Unlock", "system-lock-screen",<br>
>> >> > +                                     G_CALLBACK(view_popup_menu_on<wbr>_lock), user_data);<br>
>> >> > +        }<br>
>> >> > +    } else {<br>
>> >> > +        view_popup_add_menu_item(menu, "_Load", "media-eject",<br>
>> >> > +                                 G_CALLBACK(view_popup_menu_on<wbr>_eject), user_data);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    if (!is_dev_connected || has_single_lun(priv, lun_item->device)) {<br>
>> >> > +        view_popup_add_menu_item(menu, "_Remove", "edit-delete",<br>
>> >> > +                                 G_CALLBACK(view_popup_menu_on<wbr>_remove), user_data);<br>
>> >> > +    }<br>
>> >> > +<br>
>> >> > +    gtk_widget_show_all(menu);<br>
>> >> > +    gtk_menu_popup_at_pointer(GTK_<wbr>MENU(menu), NULL);<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static void treeview_select_current_row_by<wbr>_pos(GtkTreeView *tree_view, gint x, gint y)<br>
>> >> > +{<br>
>> >> > +    GtkTreeSelection *selection = gtk_tree_view_get_selection(GT<wbr>K_TREE_VIEW(tree_view));<br>
>> >> > +    if (gtk_tree_selection_count_sele<wbr>cted_rows(selection) <= 1) {<br>
>> >> > +        GtkTreePath *path;<br>
>> >> > +        /* Get tree path for row that was clicked */<br>
>> >> > +        if (gtk_tree_view_get_path_at_pos<wbr>(GTK_TREE_VIEW(tree_view), x, y, &path, NULL, NULL, NULL))<br>
>> >> > +        {<br>
>> >> > +            gtk_tree_selection_unselect_al<wbr>l(selection);<br>
>> >> > +            gtk_tree_selection_select_path<wbr>(selection, path);<br>
>> >> > +            gtk_tree_path_free(path);<br>
>> >> > +        }<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
>> >> > +static gboolean treeview_on_right_button_press<wbr>ed_cb(GtkWidget *view, GdkEventButton *event, gpointer user_data)<br>
>> >> > +{<br>
>> >> > +    GtkTreeView *tree_view = GTK_TREE_VIEW(view);<br>
>> >> > +    /* single click with the right mouse button */<br>
>> >> > +    if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3) {<br>
>> >> > +        /* select the row that was clicked, it will also provide the context */<br>
>> >> > +        treeview_select_current_row_by<wbr>_pos(tree_view, (gint)event->x, (gint)event->y);<br>
>> >> > +        view_popup_menu(tree_view, event, user_data);<br>
>> >> > +        return TRUE; /* we handled this */<br>
>> >> > +    } else {<br>
</div></div>>> >> > +        return FALSE; /* we did not handle this */<br>
>> >> > +    }<br>
>> >> > +}<br>
>> >> > +<br>
<span>>> >> > +static gboolean treeview_on_popup_key_pressed_<wbr>cb(GtkWidget *view, gpointer user_data)<br>
>> >> > +{<br>
</span>>> >> > +    view_popup_menu(GTK_TREE_VIEW(<wbr>view), NULL, user_data);<br>
<span>>> >> > +    return TRUE; /* we handled this */<br>
</span><span>>> >> > +}<br>
>> >> > +<br>
>> >> > +/* Add LUN dialog */<br>
>> >> > +<br>
</span>>> >> > +static void add_cd_lun_button_clicked_cb(G<wbr>tkWidget *add_cd_button, gpointer user_data)<br>
<span>>> >> > +{<br>
>> >> > +    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_d<wbr>ata);<br>
>> >> > +    SpiceUsbDeviceWidgetPrivate *priv = self->priv;<br>
</span><span>>> >> > +    GtkWidget *parent_window = gtk_widget_get_toplevel(add_cd<wbr>_button);<br>
>> >> > +    lun_properties_dialog lun_dialog;<br>
>> >> > +    gint resp;<br>
</span>>> >&g<br>
<span class="m_-3734118308932770974m_-1352333094766519113m_39639117952959130m_-3776099943896030855m_-15508575401570799m_-4751879021535101723m_-5710700431131011310m_-6775554001762653852HOEnZb"><font color="#888888"><br>
<br>
<br>
-- <br>
Marc-André Lureau<br>
</font></span></blockquote></div><br></div></div>