[gnome-boxes/wip/preferences-dialog] wIP
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes/wip/preferences-dialog] wIP
- Date: Fri, 12 Nov 2021 16:17:28 +0000 (UTC)
commit ff5051212c59a99f48faab759150d58b584165b8
Author: Felipe Borges <felipeborges gnome org>
Date: Wed Nov 10 16:57:25 2021 +0100
wIP
data/gnome-boxes.gresource.xml | 16 +-
data/ui/preferences/device-list-row.ui | 14 +
data/ui/preferences/devices-page.ui | 118 +++++++++
data/ui/preferences/preferences-window.ui | 24 ++
data/ui/preferences/resources-page.ui | 198 ++++++++++++++
data/ui/preferences/shared-folder-popover.ui | 140 ++++++++++
data/ui/preferences/shared-folder-row.ui | 25 ++
data/ui/preferences/shared-folders-widget.ui | 26 ++
data/ui/{ => preferences}/snapshot-list-row.ui | 0
data/ui/preferences/snapshots-page.ui | 54 ++++
data/ui/properties-shared-folder-row.ui | 61 -----
data/ui/properties-toolbar.ui | 62 -----
data/ui/properties-window.ui | 10 -
data/ui/resource-graph.ui | 12 -
data/ui/shared-folders.ui | 47 ----
src/actions-popover.vala | 16 +-
src/app-window.vala | 23 --
src/config-editor.vala | 105 --------
src/libvirt-machine-properties.vala | 62 +----
src/libvirt-machine.vala | 166 +-----------
src/machine.vala | 2 +-
src/meson.build | 6 +-
src/preferences/device-list-row.vala | 18 ++
src/preferences/devices-page.vala | 92 +++++++
src/preferences/meson.build | 9 +
src/preferences/preferences-window.vala | 20 ++
src/preferences/resources-page.vala | 349 +++++++++++++++++++++++++
src/preferences/shared-folders-widget.vala | 115 ++++++++
src/{ => preferences}/snapshot-list-row.vala | 5 +-
src/preferences/snapshots-page.vala | 132 ++++++++++
src/properties-toolbar.vala | 20 --
src/properties-window.vala | 11 +-
src/properties.vala | 6 -
src/resource-graph.vala | 79 ------
src/shared-folders.vala | 102 --------
src/snapshots-property.vala | 162 ------------
src/spice-display.vala | 116 +++-----
37 files changed, 1408 insertions(+), 1015 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 5fe5d7ed..97f702f1 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -21,17 +21,12 @@
<file preprocess="xml-stripblanks">ui/list-view.ui</file>
<file preprocess="xml-stripblanks">ui/list-view-row.ui</file>
<file preprocess="xml-stripblanks">ui/notification.ui</file>
- <file preprocess="xml-stripblanks">ui/properties-shared-folder-row.ui</file>
<file preprocess="xml-stripblanks">ui/properties-page-widget.ui</file>
<file preprocess="xml-stripblanks">ui/properties-toolbar.ui</file>
<file preprocess="xml-stripblanks">ui/properties-window.ui</file>
- <file preprocess="xml-stripblanks">ui/resource-graph.ui</file>
<file preprocess="xml-stripblanks">ui/searchbar.ui</file>
<file preprocess="xml-stripblanks">ui/selectionbar.ui</file>
<file preprocess="xml-stripblanks">ui/selection-toolbar.ui</file>
- <file preprocess="xml-stripblanks">ui/shared-folders.ui</file>
- <file preprocess="xml-stripblanks">ui/shared-folder-popover.ui</file>
- <file preprocess="xml-stripblanks">ui/snapshot-list-row.ui</file>
<file preprocess="xml-stripblanks">ui/machine-config-editor.ui</file>
<file preprocess="xml-stripblanks">ui/topbar.ui</file>
<file preprocess="xml-stripblanks">ui/transfer-info-row.ui</file>
@@ -52,6 +47,17 @@
<file preprocess="xml-stripblanks">ui/assistant/pages/preparation-page.ui</file>
<file preprocess="xml-stripblanks">ui/assistant/pages/setup-page.ui</file>
<file preprocess="xml-stripblanks">ui/assistant/pages/review-page.ui</file>
+ <!-- VM Preferences window -->
+ <file preprocess="xml-stripblanks">ui/preferences/devices-page.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/device-list-row.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/preferences-window.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/resources-page.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/shared-folders-widget.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/shared-folder-popover.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/shared-folder-row.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/snapshot-list-row.ui</file>
+ <file preprocess="xml-stripblanks">ui/preferences/snapshots-page.ui</file>
+
<!-- Welcome Tutorial -->
<file preprocess="xml-stripblanks">ui/welcome-tutorial.ui</file>
<file preprocess="xml-stripblanks">ui/welcome-tutorial-page.ui</file>
diff --git a/data/ui/preferences/device-list-row.ui b/data/ui/preferences/device-list-row.ui
new file mode 100644
index 00000000..5c4df00a
--- /dev/null
+++ b/data/ui/preferences/device-list-row.ui
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="BoxesDeviceListRow" parent="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+
+ <child>
+ <object class="GtkSwitch" id="toggle">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/preferences/devices-page.ui b/data/ui/preferences/devices-page.ui
new file mode 100644
index 00000000..4f2add6e
--- /dev/null
+++ b/data/ui/preferences/devices-page.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="BoxesDevicesPage" parent="HdyPreferencesPage">
+ <property name="visible">True</property>
+ <property name="icon_name">input-mouse-symbolic</property>
+ <property name="title" translatable="yes">Devices & Shares</property>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">USB Devices</property>
+
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="content"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="BoxesSharedFoldersWidget" id="shared_folders_widget"/>
+ </child>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">CDROM/DVD Drive</property>
+
+ <child>
+ <object class="HdyActionRow" id="cdrom_row">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">No CDROM/DVD image</property>
+
+ <child>
+ <object class="GtkStack" id="cdrom_stack">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="GtkButton" id="cdrom_select_button">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Select</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkMenuButton" id="cdrom_options_button">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="halign">end</property>
+ <property name="popover">cdrom_popover</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkPopover" id="cdrom_popover">
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="margin">10</property>
+
+ <child>
+ <object class="GtkModelButton">
+ <property name="visible">True</property>
+ <property name="text" translatable="yes">Remove</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkModelButton">
+ <property name="visible">True</property>
+ <property name="text" translatable="yes">Eject</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkCheckButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Make Bootable</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ <property name="width">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/preferences/preferences-window.ui b/data/ui/preferences/preferences-window.ui
new file mode 100644
index 00000000..ace4ce98
--- /dev/null
+++ b/data/ui/preferences/preferences-window.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="BoxesPreferencesWindow" parent="HdyPreferencesWindow">
+ <property name="role">boxes-vm-preferences</property>
+ <property name="modal">True</property>
+ <property name="window-position">center</property>
+ <property name="destroy-with-parent">True</property>
+ <property name="icon-name">gtk-preferences</property>
+ <property name="can-swipe-back">True</property>
+ <signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
+
+ <child>
+ <object class="BoxesResourcesPage" id="resources_page"/>
+ </child>
+
+ <child>
+ <object class="BoxesDevicesPage" id="devices_page"/>
+ </child>
+
+ <child>
+ <object class="BoxesSnapshotsPage" id="snapshots_page"/>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/preferences/resources-page.ui b/data/ui/preferences/resources-page.ui
new file mode 100644
index 00000000..3eefc13f
--- /dev/null
+++ b/data/ui/preferences/resources-page.ui
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="BoxesResourcesPage" parent="HdyPreferencesPage">
+ <property name="visible">True</property>
+ <property name="icon_name">applications-system-symbolic</property>
+ <property name="title" translatable="yes">Resources</property>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="spacing">40</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">_Name</property>
+ <property name="use-underline">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEntry" id="box_name_entry">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Resources</property>
+ <property name="description" translatable="yes">Changes to the settings bellow take effect after you
restart your box.</property>
+
+ <child>
+ <object class="HdyActionRow" id="memory_row">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Memory</property>
+ <property name="use-underline">True</property>
+
+ <child>
+ <object class="GtkSpinButton" id="memory_spin_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <signal name="input" handler="on_memory_spin_button_input"/>
+ <signal name="output" handler="on_memory_spin_button_output"/>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyActionRow" id="storage_row">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Storage</property>
+ <property name="use-underline">True</property>
+
+ <child>
+ <object class="GtkSpinButton" id="storage_spin_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <signal name="input" handler="on_storage_spin_button_input"/>
+ <signal name="output" handler="on_storage_spin_button_output"/>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">CPUs</property>
+ <property name="use-underline">True</property>
+
+ <child>
+ <object class="GtkSpinButton" id="cpus_spin_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="input_purpose">number</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyActionRow" id="acceleration_3d_row">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">3D acceleration</property>
+ <property name="use-underline">True</property>
+
+ <child>
+ <object class="GtkSwitch" id="acceleration_3d_toggle">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">Allow running in background</property>
+ <property name="use-underline">True</property>
+
+ <child>
+ <object class="GtkSwitch" id="run_in_bg_toggle">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyPreferencesGroup">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Configuration</property>
+
+ <child>
+ <object class="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Troubleshooting Logs</property>
+ <property name="subtitle" translatable="yes">Diagnose problems with your box using the log
file.</property>
+
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="on_troubleshooting_logs_button_clicked"/>
+
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">document-open-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Edit Configuration</property>
+ <property name="subtitle" translatable="yes">Edit the Libvirt domain configuration for this
box.</property>
+
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="on_edit_configuration_button_clicked"/>
+
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">document-edit-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/preferences/shared-folder-popover.ui b/data/ui/preferences/shared-folder-popover.ui
new file mode 100644
index 00000000..b6676512
--- /dev/null
+++ b/data/ui/preferences/shared-folder-popover.ui
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.19"/>
+ <template class="BoxesSharedFolderPopover" parent="GtkPopover">
+ <property name="can_focus">False</property>
+ <property name="modal">True</property>
+ <property name="position">bottom</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">36</property>
+ <property name="margin_right">16</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">23</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="valign">start</property>
+ <property name="margin_top">6</property>
+ <property name="label" translatable="yes">Local Folder</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin_top">20</property>
+ <property name="label" translatable="yes">Name</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFileChooserButton" id="file_chooser_button">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action">select-folder</property>
+ <property name="title" translatable="yes">Select Shared Folder</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_cancel"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_save"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
\ No newline at end of file
diff --git a/data/ui/preferences/shared-folder-row.ui b/data/ui/preferences/shared-folder-row.ui
new file mode 100644
index 00000000..b8147f10
--- /dev/null
+++ b/data/ui/preferences/shared-folder-row.ui
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="BoxesSharedFolderRow" parent="HdyActionRow">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="on_delete_button_clicked"/>
+ <style>
+ <class name="flat"/>
+ </style>
+
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="halign">end</property>
+ <property name="icon-name">window-close-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/preferences/shared-folders-widget.ui b/data/ui/preferences/shared-folders-widget.ui
new file mode 100644
index 00000000..cc41eb6f
--- /dev/null
+++ b/data/ui/preferences/shared-folders-widget.ui
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.19"/>
+ <template class="BoxesSharedFoldersWidget" parent="HdyPreferencesGroup">
+ <property name="visible">True</property>
+ <property name="use-markup">True</property>
+ <property name="title" translatable="yes">Shared Folders</property>
+
+ <child>
+ <object class="GtkStack">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="content"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </template>
+</interface>
diff --git a/data/ui/snapshot-list-row.ui b/data/ui/preferences/snapshot-list-row.ui
similarity index 100%
rename from data/ui/snapshot-list-row.ui
rename to data/ui/preferences/snapshot-list-row.ui
diff --git a/data/ui/preferences/snapshots-page.ui b/data/ui/preferences/snapshots-page.ui
new file mode 100644
index 00000000..092ee96e
--- /dev/null
+++ b/data/ui/preferences/snapshots-page.ui
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="BoxesSnapshotsPage" parent="HdyPreferencesPage">
+ <property name="visible">True</property>
+ <property name="icon_name">input-mouse-symbolic</property>
+ <property name="title" translatable="yes">Snapshots</property>
+
+ <child>
+ <object class="HdyPreferencesGroup" id="preferences_group">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Snapshots</property>
+
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <signal name="add" handler="update_snapshot_stack_page"/>
+ <signal name="remove" handler="update_snapshot_stack_page"/>
+ <style>
+ <class name="content"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkBox" id="activity_page">
+ <property name="visible">True</property>
+ <property name="spacing">20</property>
+ <property name="orientation">vertical</property>
+
+ <child>
+ <object class="GtkSpinner">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="activity_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Creating new snapshot…</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/properties-toolbar.ui b/data/ui/properties-toolbar.ui
index 79061d2a..3d096ba3 100644
--- a/data/ui/properties-toolbar.ui
+++ b/data/ui/properties-toolbar.ui
@@ -98,68 +98,6 @@
</packing>
</child>
- <!-- Text Editor page -->
- <child>
- <object class="GtkHeaderBar" id="config_editor">
- <property name="visible">True</property>
- <property name="show-close-button">True</property>
- <style>
- <class name="titlebar"/>
- </style>
-
- <child>
- <object class="GtkButton">
- <property name="visible">True</property>
- <property name="valign">center</property>
- <signal name="clicked" handler="on_troubleshooting_back_clicked"/>
- <style>
- <class name="image-button"/>
- </style>
-
- <child internal-child="accessible">
- <object class="AtkObject">
- <property name="accessible-name" translatable="yes">Back</property>
- </object>
- </child>
-
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="icon-name">go-previous-symbolic</property>
- </object>
- </child>
- </object>
-
- <packing>
- <property name="pack-type">start</property>
- </packing>
- </child>
-
- <child>
- <object class="GtkButton" id="apply_config_button">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="valign">center</property>
- <property name="use-underline">True</property>
- <property name="label" translatable="yes">_Apply</property>
- <signal name="clicked" handler="on_config_editor_apply_config_clicked"/>
- <style>
- <class name="text-button"/>
- <class name="suggested-action"/>
- </style>
- </object>
-
- <packing>
- <property name="pack-type">end</property>
- </packing>
- </child>
- </object>
-
- <packing>
- <property name="name">config_editor</property>
- </packing>
- </child>
-
<!-- File chooser page -->
<child>
<object class="GtkHeaderBar" id="file_chooser">
diff --git a/data/ui/properties-window.ui b/data/ui/properties-window.ui
index 8a8e63d3..f8e2dd1e 100644
--- a/data/ui/properties-window.ui
+++ b/data/ui/properties-window.ui
@@ -70,16 +70,6 @@
</packing>
</child>
- <child>
- <object class="BoxesMachineConfigEditor" id="config_editor">
- <property name="visible">True</property>
- </object>
-
- <packing>
- <property name="name">config_editor</property>
- </packing>
- </child>
-
<child>
<object class="GtkBox">
<property name="visible">True</property>
diff --git a/src/actions-popover.vala b/src/actions-popover.vala
index c953532f..4ed0e194 100644
--- a/src/actions-popover.vala
+++ b/src/actions-popover.vala
@@ -8,7 +8,7 @@
{"force_shutdown", force_shutdown_activated},
{"delete", delete_activated},
{"clone", clone_activated},
- {"properties", properties_activated},
+ {"preferences", preferences_activated},
{"restart", restart_activated},
{"send_file", send_file_activated}
@@ -98,9 +98,9 @@ public void update_for_item (CollectionItem item) {
// Properties (in separate section)
section = new GLib.Menu ();
- section.append (_("Properties"), "box.properties");
+ section.append (_("Preferences"), "box.preferences");
menu.append_section (null, section);
- var action = action_group.lookup_action ("properties") as GLib.SimpleAction;
+ var action = action_group.lookup_action ("preferences") as GLib.SimpleAction;
action.set_enabled (!importing);
bind_model (menu, null);
@@ -178,7 +178,13 @@ private void send_file_activated () {
}
- private void properties_activated () {
- window.show_properties ();
+ private void preferences_activated () {
+ var machine = window.current_item as Machine;
+
+ var preferences = new Boxes.PreferencesWindow () {
+ machine = machine,
+ transient_for = window,
+ };
+ preferences.present ();
}
}
diff --git a/src/app-window.vala b/src/app-window.vala
index 7ee13cbc..2f44e813 100644
--- a/src/app-window.vala
+++ b/src/app-window.vala
@@ -213,7 +213,6 @@ private void ui_state_changed () {
foreach (var ui in new Boxes.UI[] { topbar,
icon_view,
list_view,
- props_window,
//wizard_window,
empty_boxes }) {
ui.set_state (ui_state);
@@ -295,28 +294,6 @@ public void show_welcome_tutorial () {
}
}
- public void show_properties () {
- if (current_item != null) {
- if (ui_state == UIState.COLLECTION && selection_mode)
- selection_mode = false;
- set_state (UIState.PROPERTIES);
-
- return;
- }
-
- var selected_items = view.get_selected_items ();
-
- if (ui_state == UIState.COLLECTION && selection_mode)
- selection_mode = false;
-
- // Show for the first selected item
- foreach (var item in selected_items) {
- current_item = item;
- set_state (UIState.PROPERTIES);
- break;
- }
- }
-
public void show_send_file () {
var dialog = new Gtk.FileChooserDialog (
_("Select files to transfer"), this, Gtk.FileChooserAction.OPEN,
diff --git a/src/libvirt-machine-properties.vala b/src/libvirt-machine-properties.vala
index 44918ec5..5170b518 100644
--- a/src/libvirt-machine-properties.vala
+++ b/src/libvirt-machine-properties.vala
@@ -122,8 +122,6 @@ public string collect_logs () {
break;
case PropertiesPage.SYSTEM:
- add_resource_usage_graphs (ref list);
-
add_system_props_buttons (ref list);
get_resources_properties (ref list);
@@ -148,7 +146,8 @@ public string collect_logs () {
try {
var config = machine.domain.get_config (0);
if (!VMConfigurator.is_install_config (config) && !machine.firmware_is_efi)
- add_snapshots_property (ref list);
+ print ("NOTHIN!");
+ //add_snapshots_property (ref list);
} catch (GLib.Error e) {
warning (e.message);
}
@@ -288,49 +287,6 @@ private async void mark_recommended_resources (SizeProperty? ram_property, SizeP
return null;
}
}
-
- private void add_resource_usage_graphs (ref List<Boxes.Property> list) {
- var grid = new Gtk.Grid ();
- grid.margin_top = 20;
- grid.margin_bottom = 20;
- grid.row_spacing = 5;
- grid.column_spacing = 10;
- grid.column_homogeneous = true;
- grid.vexpand = false;
-
- // CPU
- var cpu_graph = new ResourceGraph (100);
- cpu_graph.npoints = 20;
- grid.attach (cpu_graph, 0, 0, 1, 1);
- var label = new Gtk.Label (_("CPU"));
- grid.attach (label, 0, 1, 1, 1);
-
- // I/O
- var io_graph = new ResourceGraph (104857600); // 100 MiB/s
- grid.attach (io_graph, 1, 0, 1, 1);
- label = new Gtk.Label (_("I/O"));
- grid.attach (label, 1, 1, 1, 1);
-
- // Network
- var net_graph = new ResourceGraph (1048576); // 1 MiB/s
- grid.attach (net_graph, 2, 0, 1, 1);
- label = new Gtk.Label (_("Network"));
- grid.attach (label, 2, 1, 1, 1);
-
- var stats_id = machine.stats_updated.connect (() => {
- cpu_graph.points = machine.cpu_stats;
- io_graph.points = machine.io_stats;
- net_graph.points = machine.net_stats;
- });
-
- var prop = add_property (ref list, null, grid);
- ulong flushed_id = 0;
- flushed_id = prop.flushed.connect (() => {
- machine.disconnect (stats_id);
- prop.disconnect (flushed_id);
- });
- }
-
private void add_system_props_buttons (ref List<Boxes.Property> list) {
var grid = new Gtk.Grid ();
grid.margin_bottom = 20;
@@ -373,13 +329,6 @@ private void add_system_props_buttons (ref List<Boxes.Property> list) {
machine.window.props_window.show_troubleshoot_log (log);
});
- var edit_button = new Gtk.Button.with_mnemonic (_("Edit XML"));
- edit_button.halign = Gtk.Align.END;
- grid.attach (edit_button, 2, 0, 1, 1);
- edit_button.clicked.connect (() => {
- machine.window.props_window.show_editor_view (machine);
- });
-
var prop = add_property (ref list, null, grid);
ulong flushed_id = 0;
flushed_id = prop.flushed.connect (() => {
@@ -781,13 +730,6 @@ private async void on_run_in_bg () {
});
}
- private Boxes.SnapshotsProperty add_snapshots_property (ref List<Boxes.Property> list) {
- var property = new SnapshotsProperty (machine);
- list.append (property);
-
- return property;
- }
-
private void add_3d_acceleration_property (ref List<Boxes.Property> list) {
var toggle = new Gtk.Switch ();
toggle.halign = Gtk.Align.START;
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index b11c2d45..3690b4b0 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -155,28 +155,8 @@ public override async void connect_display (Machine.ConnectFlags flags) throws G
}
}
- struct MachineStat {
- int64 timestamp;
- double cpu_time;
- double cpu_time_abs;
- double cpu_guest_percent;
- double memory_percent;
- DomainDiskStats disk;
- double disk_read;
- double disk_write;
- DomainInterfaceStats net;
- double net_read;
- double net_write;
- }
-
private uint shutdown_timeout;
- private uint stats_update_timeout;
- private Cancellable stats_cancellable;
-
- const int STATS_SIZE = 20;
- private MachineStat[] stats;
-
private bool force_stopped;
private bool saving; // Machine is being saved currently..
@@ -184,11 +164,6 @@ public override async void connect_display (Machine.ConnectFlags flags) throws G
private BoxConfig.SavedProperty[] saved_properties;
- construct {
- stats = new MachineStat[STATS_SIZE];
- stats_cancellable = new Cancellable ();
- }
-
public void update_domain_config () {
try {
domain_config = domain.get_config (GVir.DomainXMLFlags.NONE);
@@ -227,7 +202,6 @@ public async LibvirtMachine (CollectionSource source,
case DomainState.RUNNING:
case DomainState.BLOCKED:
state = MachineState.RUNNING;
- set_stats_enable (true);
break;
case DomainState.PAUSED:
state = MachineState.PAUSED;
@@ -272,9 +246,7 @@ else if (force_stopped) {
notify["state"].connect (() => {
if (state == MachineState.RUNNING) {
reconnect_display ();
- set_stats_enable (true);
- } else
- set_stats_enable (false);
+ }
});
update_domain_config ();
@@ -302,141 +274,6 @@ else if (force_stopped) {
this.config.save_properties (this, saved_properties);
}
- private void update_cpu_stat (DomainInfo info, ref MachineStat stat) {
- var prev = stats[STATS_SIZE - 1];
-
- if (info.state == DomainState.CRASHED ||
- info.state == DomainState.SHUTOFF)
- return;
-
- stat.cpu_time = info.cpuTime - prev.cpu_time_abs;
- stat.cpu_time_abs = info.cpuTime;
- // hmm, where does this x10 come from?
- var dt = (stat.timestamp - prev.timestamp) * 10;
- var percent = stat.cpu_time / dt;
- percent = percent / info.nrVirtCpu;
- stat.cpu_guest_percent = percent.clamp (0, 100);
- }
-
- private void update_mem_stat (DomainInfo info, ref MachineStat stat) {
- if (info.state != DomainState.RUNNING)
- return;
-
- stat.memory_percent = info.memory * 100.0 / info.maxMem;
- }
-
- private async void update_io_stat (DomainInfo info, MachineStat *stat) {
- if (info.state != DomainState.RUNNING)
- return;
-
- try {
- var disk = get_domain_disk ();
- if (disk == null)
- return;
-
- yield App.app.async_launcher.launch ( () => {
- stat.disk = disk.get_stats ();
- } );
- var prev = stats[STATS_SIZE - 1];
- if (prev.disk != null) {
- stat.disk_read = (stat.disk.rd_bytes - prev.disk.rd_bytes);
- stat.disk_write = (stat.disk.wr_bytes - prev.disk.wr_bytes);
- }
- } catch (GLib.Error err) {
- warning ("Failed to fetch I/O statistics for %s: %s", name, err.message);
- }
- }
-
- private async void update_net_stat (DomainInfo info, MachineStat *stat) {
- if (info.state != DomainState.RUNNING)
- return;
-
- try {
- var net = get_domain_network_interface ();
- if (net == null)
- return;
-
- yield App.app.async_launcher.launch ( () => {
- stat.net = net.get_stats ();
- } );
- var prev = stats[STATS_SIZE - 1];
- if (prev.net != null) {
- stat.net_read = (stat.net.rx_bytes - prev.net.rx_bytes);
- stat.net_write = (stat.net.tx_bytes - prev.net.tx_bytes);
- }
- } catch (GLib.Error err) {
- warning ("Failed to fetch network statistics for %s: %s", name, err.message);
- }
- }
-
- public signal void stats_updated ();
-
- public double[] cpu_stats;
- public double[] io_stats;
- public double[] net_stats;
- private async void update_stats () {
- try {
- var now = get_monotonic_time ();
- var stat = MachineStat () { timestamp = now };
- var info = yield domain.get_info_async (stats_cancellable);
-
- update_cpu_stat (info, ref stat);
- update_mem_stat (info, ref stat);
- yield update_io_stat (info, &stat);
- yield update_net_stat (info, &stat);
-
- stats = stats[1:STATS_SIZE];
- stats += stat;
-
- } catch (IOError.CANCELLED err) {
- return;
- } catch (GLib.Error err) {
- warning (err.message);
- }
-
- cpu_stats = {};
- io_stats = {};
- net_stats = {};
-
- foreach (var stat in stats) {
- cpu_stats += stat.cpu_guest_percent;
- net_stats += (stat.net_read + stat.net_write);
- io_stats += (stat.disk_read + stat.disk_write);
- }
-
- stats_updated ();
- }
-
- private void set_stats_enable (bool enable) {
- if (enable) {
- debug ("enable statistics for " + name);
- if (stats_update_timeout != 0)
- return;
-
- stats_cancellable.reset ();
- var stats_updating = false;
- stats_update_timeout = Timeout.add_seconds (1, () => {
- if (stats_updating) {
- warning ("Fetching of stats for '%s' is taking too long. Probably a libvirt bug.", name);
-
- return true;
- }
-
- stats_updating = true;
- update_stats.begin (() => { stats_updating = false; });
-
- return true;
- });
- } else {
- debug ("disable statistics for " + name);
- if (stats_update_timeout != 0) {
- stats_cancellable.cancel ();
- GLib.Source.remove (stats_update_timeout);
- }
- stats_update_timeout = 0;
- }
- }
-
public override List<Boxes.Property> get_properties (Boxes.PropertiesPage page) {
var list = properties.get_properties (page);
@@ -518,7 +355,6 @@ public override void delete (bool by_user = true) {
debug ("delete libvirt machine: " + name);
base.delete (by_user);
- set_stats_enable (false);
if (shutdown_timeout != 0) {
Source.remove (shutdown_timeout);
shutdown_timeout = 0;
diff --git a/src/machine.vala b/src/machine.vala
index cdc019a9..ce169de6 100644
--- a/src/machine.vala
+++ b/src/machine.vala
@@ -610,7 +610,7 @@ private async void try_connect_display (ConnectFlags flags = ConnectFlags.NONE)
if (this is LibvirtMachine) {
Notification.OKFunc troubleshoot = () => {
window.current_item = this;
- window.show_properties ();
+ //window.show_properties ();
var libvirt_machine = this as LibvirtMachine;
var logs = libvirt_machine.properties.collect_logs ();
diff --git a/src/meson.build b/src/meson.build
index a84bde5d..e795383b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -73,7 +73,6 @@ vala_sources = [
'machine-thumbnailer.vala',
'main.vala',
'media-manager.vala',
- 'resource-graph.vala',
'notification.vala',
'notificationbar.vala',
'os-database.vala',
@@ -88,7 +87,6 @@ vala_sources = [
'selectionbar.vala',
'selection-toolbar.vala',
'shared-folders.vala',
- 'config-editor.vala',
'transfer-info-row.vala',
'troubleshoot-view.vala',
'topbar.vala',
@@ -109,11 +107,10 @@ vala_sources = [
'empty-boxes.vala',
'tracker-iso-query.vala',
'troubleshoot-log.vala',
- 'snapshot-list-row.vala',
- 'snapshots-property.vala',
]
subdir('assistant')
+subdir('preferences')
dependencies = [
config_h,
@@ -123,7 +120,6 @@ dependencies = [
dependency ('gtk+-3.0', version: '>= 3.22.20'),
dependency ('gtk-vnc-2.0', version: '>= 0.4.4'),
dependency ('gvncpulse-1.0'),
- dependency ('gtksourceview-4'),
dependency ('libhandy-1', version: '>= 1.0.0'),
dependency ('libosinfo-1.0', version: '>= 1.7.0'),
dependency ('libsecret-1'),
diff --git a/src/preferences/device-list-row.vala b/src/preferences/device-list-row.vala
new file mode 100644
index 00000000..ec15581a
--- /dev/null
+++ b/src/preferences/device-list-row.vala
@@ -0,0 +1,18 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/device-list-row.ui")]
+private class Boxes.DeviceListRow : Hdy.ActionRow {
+ [GtkChild]
+ private unowned Gtk.Switch toggle;
+
+ public DeviceListRow (Boxes.UsbDevice device) {
+ title = device.title;
+
+ device.bind_property ("active", toggle, "active", BindingFlags.BIDIRECTIONAL);
+ }
+}
+
+class Boxes.UsbDevice : GLib.Object {
+ public string title;
+ public bool active { get; set; }
+}
diff --git a/src/preferences/devices-page.vala b/src/preferences/devices-page.vala
new file mode 100644
index 00000000..e7ddf3b8
--- /dev/null
+++ b/src/preferences/devices-page.vala
@@ -0,0 +1,92 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/devices-page.ui")]
+private class Boxes.DevicesPage : Hdy.PreferencesPage {
+ private LibvirtMachine machine;
+
+ [GtkChild]
+ private unowned Gtk.ListBox listbox;
+
+ [GtkChild]
+ private unowned Boxes.SharedFoldersWidget shared_folders_widget;
+
+ [GtkChild]
+ private unowned Hdy.ActionRow cdrom_row;
+ [GtkChild]
+ private unowned Gtk.Stack cdrom_stack;
+ [GtkChild]
+ private unowned Gtk.Button cdrom_select_button;
+ [GtkChild]
+ private unowned Gtk.MenuButton cdrom_options_button;
+
+ public void setup (LibvirtMachine machine) {
+ this.machine = machine;
+
+ setup_usb_devices_list ();
+
+ foreach (var device_config in machine.domain_config.get_devices ()) {
+ if (device_config is GVirConfig.DomainDisk) {
+ var disk_config = device_config as GVirConfig.DomainDisk;
+ var disk_type = disk_config.get_guest_device_type ();
+
+ if (disk_type == GVirConfig.DomainDiskGuestDeviceType.CDROM) {
+ setup_cdrom_row (disk_config);
+ }
+ }
+ }
+
+ shared_folders_widget.setup (machine.config.uuid);
+ }
+
+ private void setup_usb_devices_list () {
+ if (App.app.is_running_in_flatpak ()) {
+ var msg = new Gtk.Label (_("The Flatpak version of GNOME Boxes does not support USB
redirection.")) {
+ visible = true,
+ margin = 10
+ };
+ msg.get_style_context ().add_class ("dim-label");
+ listbox.add (msg);
+
+ return;
+ }
+
+ if (!machine.is_running) {
+ var msg = new Gtk.Label (_("Turn on your box to see the USB devices available for
redirection.")) {
+ visible = true,
+ margin = 10
+ };
+ msg.get_style_context ().add_class ("dim-label");
+ listbox.add (msg);
+
+ return;
+ }
+
+ if (!(machine.display is SpiceDisplay))
+ return;
+
+ var spice_display = machine.display as SpiceDisplay;
+ var model = spice_display.get_usb_devices_model ();
+ listbox.bind_model (model, add_usb_device_row);
+ }
+
+ private Gtk.Widget add_usb_device_row (GLib.Object item) {
+ var device = item as Boxes.UsbDevice;
+
+ return new Boxes.DeviceListRow (device);
+ }
+
+ private void setup_cdrom_row (GVirConfig.DomainDisk disk_config) {
+ var source = disk_config.get_source ();
+ if (source == null || source == "") {
+ cdrom_row.set_title (_("No CD/DVD image"));
+
+ cdrom_stack.set_visible_child (cdrom_select_button);
+
+ return;
+ }
+
+ cdrom_stack.set_visible_child (cdrom_options_button);
+ cdrom_row.set_title (get_utf8_basename (source));
+ }
+}
diff --git a/src/preferences/meson.build b/src/preferences/meson.build
new file mode 100644
index 00000000..05ee5378
--- /dev/null
+++ b/src/preferences/meson.build
@@ -0,0 +1,9 @@
+vala_sources += files(
+ 'devices-page.vala',
+ 'device-list-row.vala',
+ 'preferences-window.vala',
+ 'resources-page.vala',
+ 'shared-folders-widget.vala',
+ 'snapshot-list-row.vala',
+ 'snapshots-page.vala',
+)
diff --git a/src/preferences/preferences-window.vala b/src/preferences/preferences-window.vala
new file mode 100644
index 00000000..e3bbd58c
--- /dev/null
+++ b/src/preferences/preferences-window.vala
@@ -0,0 +1,20 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/preferences-window.ui")]
+private class Boxes.PreferencesWindow : Hdy.PreferencesWindow {
+ public Machine machine {
+ set {
+ resources_page.setup (value as LibvirtMachine);
+ devices_page.setup (value as LibvirtMachine);
+ snapshots_page.setup (value as LibvirtMachine);
+ }
+ }
+
+ [GtkChild]
+ private unowned Boxes.ResourcesPage resources_page;
+ [GtkChild]
+ private unowned Boxes.DevicesPage devices_page;
+ [GtkChild]
+ private unowned Boxes.SnapshotsPage snapshots_page;
+}
diff --git a/src/preferences/resources-page.vala b/src/preferences/resources-page.vala
new file mode 100644
index 00000000..52e170b3
--- /dev/null
+++ b/src/preferences/resources-page.vala
@@ -0,0 +1,349 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using GLib;
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/resources-page.ui")]
+private class Boxes.ResourcesPage : Hdy.PreferencesPage {
+ private LibvirtMachine machine;
+
+ private FileMonitor config_file_monitor;
+
+ private string logs;
+
+ [GtkChild]
+ private unowned Gtk.Entry box_name_entry;
+
+ [GtkChild]
+ private unowned Hdy.ActionRow memory_row;
+ [GtkChild]
+ private unowned Gtk.SpinButton memory_spin_button;
+ [GtkChild]
+ private unowned Hdy.ActionRow storage_row;
+ [GtkChild]
+ private unowned Gtk.SpinButton storage_spin_button;
+ [GtkChild]
+ private unowned Gtk.SpinButton cpus_spin_button;
+ [GtkChild]
+ private unowned Hdy.ActionRow acceleration_3d_row;
+ [GtkChild]
+ private unowned Gtk.Switch acceleration_3d_toggle;
+
+ [GtkChild]
+ private unowned Gtk.Switch run_in_bg_toggle;
+
+ public async void setup (LibvirtMachine machine) {
+ this.machine = machine;
+
+ bind_widget_property (box_name_entry, "text", "name");
+ bind_widget_property (run_in_bg_toggle, "active", "run-in-bg");
+ mark_recommended_resources ();
+
+ var accel3d_is_supported = yield machine.supports_accel3d ();
+ acceleration_3d_row.visible = accel3d_is_supported;
+ if (accel3d_is_supported) {
+ bind_widget_property (acceleration_3d_toggle, "active", "acceleration-3d");
+ }
+
+ try {
+ var host_topology = machine.connection.get_node_info ();
+
+ setup_memory_row (host_topology.memory);
+ setup_cpu_row (host_topology.cores * host_topology.sockets * host_topology.threads);
+ setup_storage_row ();
+ } catch (GLib.Error error) {
+ warning ("Failed to obtain virtual resources for machine: %s", error.message);
+ }
+ }
+
+ private async void mark_recommended_resources () {
+ var os = yield get_os_for_machine (machine);
+ if (os == null)
+ return;
+
+ var architecture = machine.domain_config.get_os ().get_arch ();
+ var recommended_resources = OSDatabase.get_recommended_resources_for_os (os, architecture);
+ if (recommended_resources != null) {
+ var row_subtitle = _("Recommended %s.");
+
+ memory_row.set_subtitle (row_subtitle.printf (GLib.format_size (recommended_resources.ram,
+
GLib.FormatSizeFlags.IEC_UNITS)));
+ storage_row.set_subtitle (row_subtitle.printf (GLib.format_size (recommended_resources.storage,
+
GLib.FormatSizeFlags.IEC_UNITS)));
+ }
+ }
+
+ private async Osinfo.Os? get_os_for_machine (LibvirtMachine machine) {
+ var os_id = VMConfigurator.get_os_id (machine.domain_config);
+ if (os_id == null)
+ return null;
+
+ var os_db = MediaManager.get_default ().os_db;
+ try {
+ return yield os_db.get_os_by_id (os_id);
+ } catch (OSDatabaseError error) {
+ warning ("Failed to find OS with ID '%s': %s", os_id, error.message);
+
+ return null;
+ }
+ }
+
+ private void setup_memory_row (ulong host_memory) {
+ uint64 ram = machine.domain_config.memory * Osinfo.KIBIBYTES;
+ uint64 max_ram = host_memory * Osinfo.KIBIBYTES;
+ uint64 min_ram = 128 * Osinfo.MEBIBYTES;
+
+ memory_spin_button.set_range (min_ram, max_ram);
+ memory_spin_button.set_increments (min_ram, max_ram);
+ memory_spin_button.set_value (ram);
+ }
+
+ [GtkCallback]
+ private int on_memory_spin_button_input (Gtk.SpinButton spin_button, out double new_val) {
+ var ram = spin_button.get_adjustment ().get_value ();
+
+ /* FIXME: we should be getting the value with spin_button.get_text () so we can
+ * accept user manual input. This will require to parse the text properly and
+ * convert strings such as 2.0 GiB into 2.0 * Osinfo.MEBIBYTE * 1024.
+ *
+ * As it is now, we don't support manual input, and the value can only be changed
+ * by using the + and - buttons of the GtkSpinButton. */
+ new_val = ram;
+
+ return 1;
+ }
+
+ [GtkCallback]
+ private bool on_memory_spin_button_output (Gtk.SpinButton spin_button) {
+ uint64 ram = (uint64)spin_button.get_adjustment ().get_value ();
+
+ spin_button.text = GLib.format_size (ram, GLib.FormatSizeFlags.IEC_UNITS);
+
+ return true;
+ }
+
+ private void setup_cpu_row (uint host_vcpus) {
+ var cpus = (int)machine.domain_config.get_vcpus ();
+
+ cpus_spin_button.set_range (1, host_vcpus);
+ cpus_spin_button.set_increments (1, host_vcpus);
+ cpus_spin_button.set_value (cpus);
+ }
+
+ private void setup_storage_row () {
+ if (machine.importing || machine.storage_volume == null) {
+ storage_row.hide ();
+
+ return;
+ }
+
+ try {
+ var volume_info = machine.storage_volume.get_info ();
+ var pool = get_storage_pool (machine.connection);
+ var pool_info = pool.get_info ();
+
+ var min_storage = get_minimum_disk_size (volume_info);
+ var max_storage = volume_info.allocation + pool_info.available;
+
+ if (min_storage >= max_storage) {
+ memory_row.subtitle = _("There is not enough space on your machine to increate the maximum
disk size.");
+ }
+
+ storage_spin_button.set_range (min_storage, max_storage);
+ storage_spin_button.set_increments (256 * 1000 * 1000, volume_info.allocation);
+ storage_spin_button.set_value (volume_info.allocation);
+ } catch (GLib.Error error) {
+ warning ("Failed to setup storage row: %s", error.message);
+ }
+ }
+
+ private uint64 get_minimum_disk_size (GVir.StorageVolInfo volume_info) throws GLib.Error {
+ if (machine.vm_creator == null)
+ return volume_info.capacity;
+
+ Osinfo.Resources minimum_resources = null;
+ if (machine.vm_creator.install_media.os != null) {
+ var os = machine.vm_creator.install_media.os;
+ var architecture = machine.domain_config.get_os ().get_arch ();
+ minimum_resources = OSDatabase.get_minimum_resources_for_os (os, architecture);
+ }
+
+ if (minimum_resources != null && minimum_resources.storage != -1) {
+ return minimum_resources.storage;
+ } else {
+ minimum_resources = OSDatabase.get_minimum_resources ();
+
+ return uint64.min (volume_info.capacity, minimum_resources.storage);
+ }
+ }
+
+ [GtkCallback]
+ private int on_storage_spin_button_input (Gtk.SpinButton spin_button, out double new_val) {
+ var storage = spin_button.get_adjustment ().get_value ();
+
+ /* FIXME: see on_memory_spin_button_input () */
+ new_val = storage;
+
+ return 1;
+ }
+
+ [GtkCallback]
+ private bool on_storage_spin_button_output (Gtk.SpinButton spin_button) {
+ uint64 storage = (uint64)spin_button.get_adjustment ().get_value ();
+
+ spin_button.text = GLib.format_size (storage, GLib.FormatSizeFlags.DEFAULT);
+
+ return true;
+ }
+
+ [GtkCallback]
+ private void on_troubleshooting_logs_button_clicked () {
+ if (logs == null)
+ logs = collect_logs (machine);
+
+ try {
+ var filename = get_cache ("logs", machine.domain.get_name () + ".logs");
+ File file = GLib.File.new_for_path (filename);
+
+ FileOutputStream os = file.replace (null, false, FileCreateFlags.REPLACE_DESTINATION);
+
+ os.write (logs.data);
+ GLib.AppInfo.launch_default_for_uri (file.get_uri (), null);
+
+ debug ("Showing vm configuration at %s", file.get_uri ());
+ GLib.stdout.printf (logs + "\n");
+ } catch (GLib.Error error) {
+ warning ("Failed to collect machine logs: %s", error.message);
+ }
+ }
+
+ [GtkCallback]
+ private async void on_edit_configuration_button_clicked () {
+ var message_dialog = new Gtk.MessageDialog (get_toplevel () as Gtk.Window,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.YES_NO,
+ _("Editing your box configuration can cause issues to
the operating system of your box. Would you like to create a snapshot to recover from your changes?"));
+ message_dialog.show_all ();
+ message_dialog.response.connect ((response) => {
+ if (response == Gtk.ResponseType.YES) {
+ debug ("Creating snapshot...");
+
+ // TODO: create a better snapshot description label.
+ machine.create_snapshot ();
+ }
+
+ message_dialog.destroy ();
+ open_config_file ();
+ });
+ }
+
+ private void open_config_file () {
+ try {
+ var domain_xml = machine.domain_config.to_xml ();
+ File file = GLib.File.new_for_path (Path.build_filename (Environment.get_home_dir (),
+ "." + machine.domain.get_name () + ".draft.txt"));
+ FileOutputStream os = file.replace (null, false, FileCreateFlags.REPLACE_DESTINATION);
+ os.write (domain_xml.data);
+
+ config_file_monitor = file.monitor_file (GLib.FileMonitorFlags.NONE, null);
+ config_file_monitor.changed.connect (on_domain_configuration_edited);
+
+ GLib.AppInfo.launch_default_for_uri (file.get_uri (), null);
+ } catch (GLib.Error error) {
+ warning ("Failed to edit VM configuration: %s", error.message);
+ }
+ }
+
+ private void on_domain_configuration_edited (File file,
+ File? other_file,
+ FileMonitorEvent event_type) {
+ if (event_type != FileMonitorEvent.CHANGED &&
+ event_type != FileMonitorEvent.CHANGES_DONE_HINT) {
+ return;
+ }
+
+ try {
+ uint8[] contents;
+ string etag_out;
+ file.load_contents (null, out contents, out etag_out);
+
+ GVirConfig.Domain new_config = new GVirConfig.Domain.from_xml ((string)contents);
+ var edited_tag = "<edited>%s</edited>".printf (new DateTime.now_local ().to_string ());
+ new_config.set_custom_xml (edited_tag,
+ "edited",
+ "https://wiki.gnome.org/Apps/Boxes/edited");
+ machine.domain.set_config (new_config);
+
+ debug ("Overriding configuration for %s", machine.domain.get_name ());
+ } catch (GLib.Error error) {
+ warning ("Failed to load new domain configuration: %s", error.message);
+
+ var message_dialog = new Gtk.MessageDialog (App.app.main_window,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ _("Failed to save domain configuration: %s"),
+ error.message);
+ message_dialog.run ();
+ message_dialog.destroy ();
+ }
+ }
+
+ private void bind_widget_property (Gtk.Widget widget, string widget_property, string machine_property) {
+ machine.bind_property (machine_property, widget, widget_property, BindingFlags.BIDIRECTIONAL);
+
+ var value = GLib.Value (machine.get_class ().find_property (machine_property).value_type);
+ machine.get_property (machine_property, ref value);
+ widget.set_property (widget_property, value);
+ }
+
+ private string collect_logs (LibvirtMachine machine) {
+ var builder = new StringBuilder ();
+
+ builder.append_printf ("Broker URL: %s\n", machine.source.uri);
+ builder.append_printf ("Domain: %s\n", machine.domain.get_name ());
+ builder.append_printf ("UUID: %s\n", machine.domain.get_uuid ());
+ builder.append_printf ("Persistent: %s\n", machine.domain.get_persistent () ? "yes" : "no");
+ try {
+ var info = machine.domain.get_info ();
+ builder.append_printf ("Cpu time: %"+uint64.FORMAT_MODIFIER+"d\n", info.cpuTime);
+ builder.append_printf ("Memory: %"+uint64.FORMAT_MODIFIER+"d KiB\n", info.memory);
+ builder.append_printf ("Max memory: %"+uint64.FORMAT_MODIFIER+"d KiB\n",
+ machine.connection.get_node_info ().memory);
+ builder.append_printf ("CPUs: %d\n", info.nrVirtCpu);
+ builder.append_printf ("State: %s\n", info.state.to_string ());
+ } catch (GLib.Error e) {
+ }
+
+ if (machine.display != null)
+ machine.display.collect_logs (builder);
+
+
+ try {
+ var conf = machine.domain.get_config (GVir.DomainXMLFlags.NONE);
+ builder.append_printf ("\nDomain config:\n");
+ builder.append_printf ("------------------------------------------------------------\n");
+
+ builder.append (conf.to_xml ());
+ builder.append_printf ("\n" +
+ "------------------------------------------------------------\n");
+ } catch (GLib.Error error) {
+ }
+
+ try {
+ var logfile = Path.build_filename (Environment.get_user_cache_dir (),
+ "libvirt/qemu/log",
+ machine.domain.get_name () + ".log");
+ string data;
+ FileUtils.get_contents (logfile, out data);
+ builder.append_printf ("\nQEMU log:\n");
+ builder.append_printf ("------------------------------------------------------------\n");
+
+ builder.append (data);
+ builder.append_printf ("------------------------------------------------------------\n");
+ } catch (GLib.Error e) {
+ }
+
+ return builder.str;
+ }
+}
diff --git a/src/preferences/shared-folders-widget.vala b/src/preferences/shared-folders-widget.vala
new file mode 100644
index 00000000..cca1fe2b
--- /dev/null
+++ b/src/preferences/shared-folders-widget.vala
@@ -0,0 +1,115 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/shared-folder-row.ui")]
+private class Boxes.SharedFolderRow : Hdy.ActionRow {
+ public signal void removed (SharedFolder folder);
+
+ public SharedFolder folder { get; private set; }
+
+ public SharedFolderRow (SharedFolder folder) {
+ this.folder = folder;
+
+ folder.bind_property ("path", this, "title", BindingFlags.SYNC_CREATE);
+ folder.bind_property ("name", this, "subtitle", BindingFlags.SYNC_CREATE);
+ }
+
+ [GtkCallback]
+ private void on_delete_button_clicked () {
+ removed (folder);
+ }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/shared-folders-widget.ui")]
+private class Boxes.SharedFoldersWidget: Hdy.PreferencesGroup {
+ private string machine_uuid;
+
+ private SharedFoldersManager manager = SharedFoldersManager.get_default ();
+
+ private GLib.ListStore list_model;
+
+ private Boxes.SharedFolderPopover popover;
+
+ [GtkChild]
+ private unowned Gtk.ListBox listbox;
+
+ construct {
+ popover = new SharedFolderPopover ();
+ popover.saved.connect (on_popover_saved);
+ }
+
+ public void setup (string machine_uuid) {
+ this.machine_uuid = machine_uuid;
+
+ list_model = manager.get_folders (machine_uuid);
+ list_model.items_changed.connect (on_list_updated);
+ listbox.bind_model (list_model, create_shared_folder_row);
+
+ var add_button = new Gtk.MenuButton () {
+ visible = true,
+ image = new Gtk.Image () {
+ icon_name = "list-add-symbolic"
+ },
+ popover = popover
+ };
+ add_button.get_style_context ().add_class ("flat");
+ listbox.add (add_button);
+
+ on_list_updated ();
+ }
+
+ private bool on_popover_saved (string path, string? name) {
+ return manager.add_item (new SharedFolder (machine_uuid, path, name));
+ }
+
+ private Gtk.Widget create_shared_folder_row (Object item) {
+ var folder = item as SharedFolder;
+ var row = new SharedFolderRow (folder);
+
+ row.removed.connect (manager.remove_item);
+
+ return row;
+ }
+
+ private void on_list_updated () {
+ if (list_model.get_n_items () == 0)
+ description = _("Use the button bellow to add your first shared folder.");
+ else
+ description = null;
+ }
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/shared-folder-popover.ui")]
+private class Boxes.SharedFolderPopover: Gtk.Popover {
+ public signal bool saved (string path, string name);
+
+ [GtkChild]
+ public unowned Gtk.FileChooserButton file_chooser_button;
+ [GtkChild]
+ public unowned Gtk.Entry name_entry;
+
+ construct {
+ var default_path = Environment.get_user_special_dir (UserDirectory.PUBLIC_SHARE);
+ file_chooser_button.set_current_folder (default_path);
+ }
+
+ [GtkCallback]
+ public void on_cancel (Gtk.Button cancel_button) {
+ popdown ();
+ }
+
+ [GtkCallback]
+ public void on_save (Gtk.Button save_button) {
+ var uri = file_chooser_button.get_uri ();
+ File file = File.new_for_uri (uri);
+ var name = name_entry.get_text ();
+
+ if (uri != null) {
+ if (name == "")
+ name = file.get_basename ();
+
+ saved (file.get_path (), name);
+ }
+
+ popdown ();
+ }
+}
diff --git a/src/snapshot-list-row.vala b/src/preferences/snapshot-list-row.vala
similarity index 97%
rename from src/snapshot-list-row.vala
rename to src/preferences/snapshot-list-row.vala
index dee9b007..71cce9e7 100644
--- a/src/snapshot-list-row.vala
+++ b/src/preferences/snapshot-list-row.vala
@@ -1,6 +1,6 @@
// This file is part of GNOME Boxes. License: LGPLv2+
-[GtkTemplate (ui = "/org/gnome/Boxes/ui/snapshot-list-row.ui")]
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/snapshot-list-row.ui")]
private class Boxes.SnapshotListRow : Gtk.ListBoxRow {
public GVir.DomainSnapshot snapshot;
public string activity_message { get; set; default = ""; }
@@ -82,7 +82,8 @@ public override bool draw (Cairo.Context ct) {
ct.line_to (height / 2.0 + 0.5, height / 2.0);
ct.stroke ();
}
- if (index < parent_size - 1) {
+ // this row + the SnapshotPage's add_button row
+ if (index < parent_size - 2) {
ct.move_to (height / 2.0 + 0.5, height / 2.0);
ct.line_to (height / 2.0 + 0.5, height + 1);
ct.stroke ();
diff --git a/src/preferences/snapshots-page.vala b/src/preferences/snapshots-page.vala
new file mode 100644
index 00000000..afd156e0
--- /dev/null
+++ b/src/preferences/snapshots-page.vala
@@ -0,0 +1,132 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/snapshots-page.ui")]
+private class Boxes.SnapshotsPage : Hdy.PreferencesPage {
+ private LibvirtMachine machine;
+
+ [GtkChild]
+ private unowned Hdy.PreferencesGroup preferences_group;
+
+ [GtkChild]
+ private unowned Gtk.Stack stack;
+ [GtkChild]
+ private unowned Gtk.ListBox listbox;
+ [GtkChild]
+ private unowned Gtk.Box activity_page;
+ [GtkChild]
+ private unowned Gtk.Label activity_label;
+
+ private Gtk.Button add_button;
+
+ private string? activity {
+ set {
+ if (value == null) {
+ stack.visible_child = listbox;
+ } else {
+ activity_label.label = value;
+ stack.visible_child = activity_page;
+ }
+ }
+ }
+
+ public void setup (LibvirtMachine machine) {
+ this.machine = machine;
+
+ listbox.set_sort_func (config_sort_func);
+
+ destroy.connect (() => { fetch_snapshots_cancellable.cancel (); });
+ fetch_snapshots.begin ();
+
+ add_button = new Gtk.Button () {
+ visible = true,
+ image = new Gtk.Image () {
+ visible = true,
+ icon_name = "list-add-symbolic"
+ }
+ };
+ add_button.get_style_context ().add_class ("flat");
+ add_button.clicked.connect (create_snapshot);
+ listbox.add (add_button);
+
+ update_snapshot_stack_page ();
+ }
+
+ private GLib.Cancellable fetch_snapshots_cancellable = new GLib.Cancellable ();
+ private async void fetch_snapshots () {
+ try {
+ yield machine.domain.fetch_snapshots_async (GVir.DomainSnapshotListFlags.ALL,
+ fetch_snapshots_cancellable);
+ var snapshots = machine.domain.get_snapshots ();
+ foreach (var snapshot in snapshots) {
+ add_snapshot_row (snapshot);
+ }
+ } catch (GLib.Error e) {
+ warning ("Could not fetch snapshots: %s", e.message);
+ }
+ }
+
+ private void add_snapshot_row (GVir.DomainSnapshot snapshot) {
+ var row = new SnapshotListRow (snapshot, machine);
+ row.notify["activity-message"].connect (row_activity_changed);
+
+ listbox.add (row);
+ }
+
+ private int config_sort_func (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
+ if (row1.get_child () == add_button)
+ return 1;
+ if (row2.get_child () == add_button)
+ return 1;
+
+ try {
+ var snapshot_row1 = row1 as SnapshotListRow;
+ var snapshot_row2 = row2 as SnapshotListRow;
+
+ var conf1 = snapshot_row1.snapshot.get_config (0);
+ var conf2 = snapshot_row2.snapshot.get_config (0);
+ if (conf1.get_creation_time () < conf2.get_creation_time ())
+ return -1;
+ else
+ return 1;
+ } catch (GLib.Error e) {
+ warning ("Failed to fetch snapshot config: %s", e.message);
+
+ return 0;
+ }
+ }
+
+ private async void create_snapshot () {
+ if (machine.state == Machine.MachineState.RUNNING)
+ this.activity = _("Creating new snapshot…");
+
+ try {
+ var new_snapshot = yield machine.create_snapshot ();
+ add_snapshot_row (new_snapshot);
+ } catch (GLib.Error e) {
+ var msg = _("Failed to create snapshot of %s").printf (machine.name);
+ machine.window.notificationbar.display_error (msg);
+ warning (e.message);
+ }
+ this.activity = null;
+
+ update_snapshot_stack_page ();
+ }
+
+ private void row_activity_changed (GLib.Object source, GLib.ParamSpec param_spec) {
+ var row = source as SnapshotListRow;
+ this.activity = row.activity_message;
+ }
+
+ [GtkCallback]
+ private void update_snapshot_stack_page () {
+ var num_rows = listbox.get_children ().length ();
+
+ // we need to account for the "+" button
+ if (num_rows > 1) {
+ preferences_group.description = null;
+ } else {
+ preferences_group.description = _("Use the button bellow to create your first snapshot.");
+ }
+ }
+}
diff --git a/src/properties-toolbar.vala b/src/properties-toolbar.vala
index d8bc525e..f45ba9b9 100644
--- a/src/properties-toolbar.vala
+++ b/src/properties-toolbar.vala
@@ -16,11 +16,6 @@
[GtkChild]
public unowned Gtk.HeaderBar main;
- [GtkChild]
- public unowned Gtk.HeaderBar config_editor;
- [GtkChild]
- public unowned Gtk.Button apply_config_button;
-
[GtkChild]
public unowned Gtk.Button troubleshooting_back_button;
@@ -43,7 +38,6 @@ public void setup_ui (AppWindow window, PropertiesWindow props_window) {
this.props_window = props_window;
window.notify["ui-state"].connect (ui_state_changed);
- apply_config_button.notify["sensitive"].connect (on_apply_config_editor_sensitivity_changed);
}
public void click_back_button () {
@@ -63,20 +57,6 @@ private void on_copy_clipboard_clicked () requires (page == PropsWindowPage.TROU
props_window.copy_troubleshoot_log_to_clipboard ();
}
- [GtkCallback]
- private void on_config_editor_apply_config_clicked () {
- props_window.config_editor.apply ();
- apply_config_button.sensitive = false;
- }
-
- private void on_apply_config_editor_sensitivity_changed () {
- if (apply_config_button.sensitive) {
- config_editor.set_title ("*" + config_editor.get_title ());
- } else {
- config_editor.set_title (config_editor.get_title ().substring (1));
- }
- }
-
[GtkCallback]
private void on_title_entry_changed () {
window.current_item.name = title_entry.text;
diff --git a/src/properties-window.vala b/src/properties-window.vala
index 60b4aca0..006ad9fb 100644
--- a/src/properties-window.vala
+++ b/src/properties-window.vala
@@ -12,7 +12,7 @@
[GtkTemplate (ui = "/org/gnome/Boxes/ui/properties-window.ui")]
private class Boxes.PropertiesWindow: Gtk.Window, Boxes.UI {
- public const string[] page_names = { "main", "troubleshoot_log", "file_chooser", "config_editor" };
+ public const string[] page_names = { "main", "troubleshoot_log", "file_chooser" };
public UIState previous_ui_state { get; protected set; }
public UIState ui_state { get; protected set; }
@@ -34,8 +34,6 @@
public unowned Properties properties;
[GtkChild]
public unowned TroubleshootLog troubleshoot_log;
- [GtkChild]
- public unowned MachineConfigEditor config_editor;
public Gtk.FileChooserNative file_chooser;
[GtkChild]
@@ -68,13 +66,6 @@ public void show_troubleshoot_log (string log) {
page = PropsWindowPage.TROUBLESHOOTING_LOG;
}
- public void show_editor_view (LibvirtMachine machine) {
- page = PropsWindowPage.TEXT_EDITOR;
- config_editor.setup (machine, topbar.apply_config_button);
-
- topbar.config_editor.set_title (machine.name);
- }
-
public void show_file_chooser (owned FileChosenFunc file_chosen_func) {
page = PropsWindowPage.FILE_CHOOSER;
var res = file_chooser.run ();
diff --git a/src/properties.vala b/src/properties.vala
index e636a378..a73f0210 100644
--- a/src/properties.vala
+++ b/src/properties.vala
@@ -16,7 +16,6 @@
private AppWindow window;
- private ulong stats_id;
private bool restore_fullscreen;
construct {
@@ -59,11 +58,6 @@ public void setup_ui (AppWindow window, PropertiesWindow dialog) {
}
private void ui_state_changed () {
- if (stats_id != 0) {
- window.current_item.disconnect (stats_id);
- stats_id = 0;
- }
-
if (ui_state == UIState.PROPERTIES) {
restore_fullscreen = (previous_ui_state == UIState.DISPLAY && window.fullscreened);
window.fullscreened = false;
diff --git a/src/shared-folders.vala b/src/shared-folders.vala
index b3c7c15c..ce14b132 100644
--- a/src/shared-folders.vala
+++ b/src/shared-folders.vala
@@ -179,105 +179,3 @@ private static string get_shared_folder_real_path (SharedFolder folder) {
folder.machine_uuid);
}
}
-
-[GtkTemplate (ui = "/org/gnome/Boxes/ui/properties-shared-folder-row.ui")]
-private class Boxes.SharedFolderRow : Gtk.ListBoxRow {
- public signal void removed (SharedFolder folder);
- [GtkChild]
- private unowned Gtk.Label folder_path_label;
- [GtkChild]
- private unowned Gtk.Label folder_name_label;
-
- public SharedFolder folder { get; private set; }
-
- public SharedFolderRow (SharedFolder folder) {
- this.folder = folder;
-
- folder.bind_property ("path", folder_path_label, "label", BindingFlags.SYNC_CREATE);
- folder.bind_property ("name", folder_name_label, "label", BindingFlags.SYNC_CREATE);
- }
-
- [GtkCallback]
- private void on_delete_button_clicked () {
- removed (folder);
- }
-}
-
-[GtkTemplate (ui = "/org/gnome/Boxes/ui/shared-folders.ui")]
-private class Boxes.SharedFoldersWidget: Gtk.Frame {
- private string machine_uuid;
-
- private SharedFoldersManager manager = SharedFoldersManager.get_default ();
-
- private GLib.ListStore list_model;
-
- private Boxes.SharedFolderPopover popover;
- [GtkChild]
- private unowned Gtk.ListBox listbox;
-
- public SharedFoldersWidget (string machine_uuid) {
- this.machine_uuid = machine_uuid;
-
- list_model = manager.get_folders (machine_uuid);
- listbox.bind_model (list_model, create_shared_folder_row);
-
- popover = new SharedFolderPopover ();
- popover.saved.connect (on_popover_saved);
- }
-
- private bool on_popover_saved (string path, string? name) {
- return manager.add_item (new SharedFolder (machine_uuid, path, name));
- }
-
- [GtkCallback]
- private void on_add_button_clicked (Gtk.Button button) {
- popover.relative_to = button;
-
- popover.popup ();
- }
-
- private Gtk.Widget create_shared_folder_row (Object item) {
- var folder = item as SharedFolder;
- var row = new SharedFolderRow (folder);
-
- row.removed.connect (manager.remove_item);
-
- return row;
- }
-}
-
-[GtkTemplate (ui = "/org/gnome/Boxes/ui/shared-folder-popover.ui")]
-private class Boxes.SharedFolderPopover: Gtk.Popover {
- public signal bool saved (string path, string name);
-
- [GtkChild]
- public unowned Gtk.FileChooserButton file_chooser_button;
- [GtkChild]
- public unowned Gtk.Entry name_entry;
-
- construct {
- var default_path = Environment.get_user_special_dir (UserDirectory.PUBLIC_SHARE);
- file_chooser_button.set_current_folder (default_path);
- }
-
- [GtkCallback]
- public void on_cancel (Gtk.Button cancel_button) {
- popdown ();
- }
-
- [GtkCallback]
- public void on_save (Gtk.Button save_button) {
- var uri = file_chooser_button.get_uri ();
- File file = File.new_for_uri (uri);
- var name = name_entry.get_text ();
-
- if (uri != null) {
- if (name == "")
- name = file.get_basename ();
-
- saved (file.get_path (), name);
- }
-
- popdown ();
- }
-}
diff --git a/src/spice-display.vala b/src/spice-display.vala
index f01d084e..3b50083c 100644
--- a/src/spice-display.vala
+++ b/src/spice-display.vala
@@ -383,38 +383,9 @@ private void on_new_file_transfer (Spice.MainChannel main_channel, Object transf
break;
case PropertiesPage.DEVICES:
- try {
- var manager = UsbDeviceManager.get (session);
- var devs = get_usb_devices (manager);
-
- if (connected && devs.length > 0) {
- devs.sort ( (a, b) => {
- string str_a = a.get_description (" %1$s %2$s");
- string str_b = b.get_description (" %1$s %2$s");
-
- return strcmp (str_a, str_b);
- });
-
- var frame = create_usb_frame (manager, devs);
-
- var usb_property = add_property (ref list, _("USB devices"), new Gtk.Label (""), frame);
-
- manager.device_added.connect ((manager, dev) => {
- usb_property.refresh_properties ();
- });
- manager.device_removed.connect ((manager, dev) => {
- usb_property.refresh_properties ();
- });
- }
- } catch (GLib.Error error) {
- }
-
if (webdav_channel == null || !webdav_channel.port_opened)
break;
- var frame = create_shared_folders_frame ();
- add_property (ref list, _("Folder Shares"), new Gtk.Label (""), frame);
-
break;
}
@@ -428,8 +399,8 @@ public override void send_keys (uint[] keyvals) {
display.send_keys (keyvals, DisplayKeyEvent.CLICK);
}
- private GLib.GenericArray<UsbDevice> get_usb_devices (UsbDeviceManager manager) {
- GLib.GenericArray<UsbDevice> ret = new GLib.GenericArray<UsbDevice> ();
+ private GLib.GenericArray<Spice.UsbDevice> get_usb_devices (UsbDeviceManager manager) {
+ GLib.GenericArray<Spice.UsbDevice> ret = new GLib.GenericArray<Spice.UsbDevice> ();
var devs = manager.get_devices ();
if (Environment.get_variable ("BOXES_USB_REDIR_ALL") != null)
@@ -474,57 +445,56 @@ public override void send_keys (uint[] keyvals) {
return ret;
}
- private Gtk.Frame create_usb_frame (UsbDeviceManager manager, GLib.GenericArray<UsbDevice> devs) {
- var frame = new Gtk.Frame (null);
- var listbox = new Gtk.ListBox ();
- listbox.hexpand = true;
- frame.add (listbox);
+ public GLib.ListStore get_usb_devices_model () {
+ GLib.ListStore model = new GLib.ListStore (typeof (Boxes.UsbDevice));
+
+ GLib.GenericArray<Spice.UsbDevice> devs = new GLib.GenericArray<Spice.UsbDevice> ();
+ UsbDeviceManager manager;
+ try {
+ manager = UsbDeviceManager.get (session);
+ devs = get_usb_devices (manager);
+ } catch (GLib.Error error) {
+ warning ("Failed to obtain usb devices list: %s", error.message);
+
+ return model;
+ }
for (int i = 0; i < devs.length; i++) {
var dev = devs[i];
- var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
- hbox.margin_start = 12;
- hbox.margin_end = 12;
- hbox.margin_top = 6;
- hbox.margin_bottom = 6;
- var label = new Gtk.Label (dev.get_description ("%1$s %2$s"));
- label.halign = Gtk.Align.START;
- hbox.pack_start (label, true, true, 0);
- var dev_toggle = new Gtk.Switch ();
- dev_toggle.halign = Gtk.Align.END;
- hbox.pack_start (dev_toggle, true, true, 0);
- listbox.prepend (hbox);
-
- dev_toggle.active = manager.is_device_connected (dev);
-
- dev_toggle.notify["active"].connect ( () => {
- if (dev_toggle.active) {
- manager.connect_device_async.begin (dev, null, (obj, res) => {
- try {
- manager.connect_device_async.end (res);
- } catch (GLib.Error err) {
- dev_toggle.active = false;
- var device_desc = dev.get_description ("%1$s %2$s");
- var box_name = get_box_name ();
- var msg = _("Redirection of USB device “%s” for “%s” failed");
- got_error (msg.printf (device_desc, box_name));
- debug ("Error connecting %s to %s: %s",
- device_desc,
- box_name, err.message);
- }
- });
- } else {
+ var usb_device = new Boxes.UsbDevice () {
+ title = dev.get_description ("%1$s %2$s"),
+ active = manager.is_device_connected (dev),
+ };
+
+ usb_device.notify["active"].connect (() => {
+ if (!usb_device.active) {
manager.disconnect_device (dev);
+
+ return;
}
+
+ manager.connect_device_async.begin (dev, null, (obj, res) => {
+ try {
+ manager.connect_device_async.end (res);
+ } catch (GLib.Error err) {
+ usb_device.active = false;
+ var device_desc = dev.get_description ("%1$s %2$s");
+ var box_name = get_box_name ();
+ var msg = _("Redirection of USB device “%s” for “%s” failed");
+
+ got_error (msg.printf (device_desc, box_name));
+ debug ("Error connecting %s to %s: %s",
+ device_desc,
+ box_name, err.message);
+ }
+ });
});
- }
- return frame;
- }
+ model.append (usb_device);
+ }
- private Gtk.Frame create_shared_folders_frame () {
- return new SharedFoldersWidget (machine.config.uuid);
+ return model;
}
private bool is_usb_kbd_or_mouse (uint8 class, uint8 subclass, uint8 protocol) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]