[gnome-boxes/wip/preferences-dialog] WIP




commit 514eaa08db0f0e06e055ca23e0791b426540bf3d
Author: Felipe Borges <felipeborges gnome org>
Date:   Mon Oct 18 11:15:10 2021 +0200

    WIP

 build-aux/flatpak/org.gnome.Boxes.json         | 411 +++++++++++++------------
 data/gnome-boxes.gresource.xml                 |  14 +-
 data/ui/preferences/devices-page.ui            | 108 +++++++
 data/ui/preferences/preferences-window.ui      |  24 ++
 data/ui/preferences/resources-page.ui          | 286 +++++++++++++++++
 data/ui/preferences/shared-folder-popover.ui   | 140 +++++++++
 data/ui/preferences/shared-folder-row.ui       |  25 ++
 data/ui/preferences/shared-folders-widget.ui   |  24 ++
 data/ui/{ => preferences}/snapshot-list-row.ui |   0
 data/ui/preferences/snapshots-page.ui          |  59 ++++
 data/ui/properties-shared-folder-row.ui        |  61 ----
 data/ui/shared-folders.ui                      |  47 ---
 src/actions-popover.vala                       |  15 +-
 src/app-window.vala                            |  22 --
 src/libvirt-machine-properties.vala            |  16 +-
 src/libvirt-machine.vala                       |  16 +-
 src/machine.vala                               |   2 +-
 src/meson.build                                |   3 +-
 src/preferences/devices-page.vala              |  54 ++++
 src/preferences/meson.build                    |   8 +
 src/preferences/preferences-window.vala        |  20 ++
 src/preferences/resources-page.vala            | 258 ++++++++++++++++
 src/preferences/shared-folders-widget.vala     | 109 +++++++
 src/{ => preferences}/snapshot-list-row.vala   |   2 +-
 src/preferences/snapshots-page.vala            | 111 +++++++
 src/shared-folders.vala                        | 102 ------
 src/snapshots-property.vala                    | 162 ----------
 src/spice-display.vala                         |   7 -
 28 files changed, 1471 insertions(+), 635 deletions(-)
---
diff --git a/build-aux/flatpak/org.gnome.Boxes.json b/build-aux/flatpak/org.gnome.Boxes.json
index 0ce656d7..e2f49f20 100644
--- a/build-aux/flatpak/org.gnome.Boxes.json
+++ b/build-aux/flatpak/org.gnome.Boxes.json
@@ -1,11 +1,15 @@
 {
-    "app-id": "org.gnome.BoxesDevel",
-    "runtime": "org.gnome.Platform",
-    "runtime-version": "master",
-    "sdk": "org.gnome.Sdk",
-    "command": "gnome-boxes",
-    "tags": ["devel", "development", "nightly"],
-    "finish-args": [
+    "app-id" : "org.gnome.BoxesDevel",
+    "runtime" : "org.gnome.Platform",
+    "runtime-version" : "master",
+    "sdk" : "org.gnome.Sdk",
+    "command" : "gnome-boxes",
+    "tags" : [
+        "devel",
+        "development",
+        "nightly"
+    ],
+    "finish-args" : [
         "--share=ipc",
         "--socket=fallback-x11",
         "--socket=x11",
@@ -25,10 +29,12 @@
         "--env=DCONF_USER_CONFIG_DIR=.config/dconf",
         "--add-policy=Tracker3.dbus:org.freedesktop.Tracker3.Miner.Files=tracker:Software"
     ],
-    "modules": [
+    "modules" : [
         {
             "name" : "intltool",
-            "cleanup" : [ "*" ],
+            "cleanup" : [
+                "*"
+            ],
             "sources" : [
                 {
                     "type" : "archive",
@@ -38,61 +44,63 @@
             ]
         },
         {
-            "name": "libvirt-glib",
-            "buildsystem": "meson",
-            "sources": [
+            "name" : "libvirt-glib",
+            "buildsystem" : "meson",
+            "sources" : [
                 {
-                    "type": "archive",
-                    "url": "https://libvirt.org/sources/glib/libvirt-glib-4.0.0.tar.xz";,
-                    "sha256": "8423f7069daa476307321d1c11e2ecc285340cd32ca9fc05207762843edeacbd"
+                    "type" : "archive",
+                    "url" : "https://libvirt.org/sources/glib/libvirt-glib-4.0.0.tar.xz";,
+                    "sha256" : "8423f7069daa476307321d1c11e2ecc285340cd32ca9fc05207762843edeacbd"
                 }
             ],
-            "modules": [
+            "modules" : [
                 {
-                    "name": "libnl",
-                    "sources": [
+                    "name" : "libnl",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://github.com/thom311/libnl/releases/download/libnl3_5_0/libnl-3.5.0.tar.gz";,
-                            "sha256": "352133ec9545da76f77e70ccb48c9d7e5324d67f6474744647a7ed382b5e05fa"
+                            "type" : "archive",
+                            "url" : 
"https://github.com/thom311/libnl/releases/download/libnl3_5_0/libnl-3.5.0.tar.gz";,
+                            "sha256" : "352133ec9545da76f77e70ccb48c9d7e5324d67f6474744647a7ed382b5e05fa"
                         }
                     ]
                 },
                 {
-                    "name": "yajl",
-                    "buildsystem": "cmake-ninja",
-                    "builddir": true,
-                    "config-opts": [
+                    "name" : "yajl",
+                    "buildsystem" : "cmake-ninja",
+                    "builddir" : true,
+                    "config-opts" : [
                         "-DCMAKE_BUILD_TYPE=Release"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://github.com/lloyd/yajl/archive/2.1.0.tar.gz";,
-                            "sha256": "3fb73364a5a30efe615046d07e6db9d09fd2b41c763c5f7d3bfb121cd5c5ac5a"
+                            "type" : "archive",
+                            "url" : "https://github.com/lloyd/yajl/archive/2.1.0.tar.gz";,
+                            "sha256" : "3fb73364a5a30efe615046d07e6db9d09fd2b41c763c5f7d3bfb121cd5c5ac5a"
                         }
                     ]
                 },
                 {
-                    "name": "portablexdr",
-                    "buildsystem": "autotools",
-                    "config-opts": ["--disable-static"],
-                    "sources": [
+                    "name" : "portablexdr",
+                    "buildsystem" : "autotools",
+                    "config-opts" : [
+                        "--disable-static"
+                    ],
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://people.redhat.com/~rjones/portablexdr/files/portablexdr-4.9.1.tar.gz";,
-                            "sha256": "5cf4bdd153cf4d44eaf10b725f451d0cfadc070b4b9a9ccfb64094b8f78de72c"
+                            "type" : "archive",
+                            "url" : 
"https://people.redhat.com/~rjones/portablexdr/files/portablexdr-4.9.1.tar.gz";,
+                            "sha256" : "5cf4bdd153cf4d44eaf10b725f451d0cfadc070b4b9a9ccfb64094b8f78de72c"
                         },
                         {
-                            "type": "script",
-                            "dest-filename": "autogen.sh",
-                            "commands": [
+                            "type" : "script",
+                            "dest-filename" : "autogen.sh",
+                            "commands" : [
                                 "AUTOMAKE=\"automake --foreign\" autoreconf -vfi"
                             ]
                         },
                         {
-                            "type": "shell",
-                            "commands": [
+                            "type" : "shell",
+                            "commands" : [
                                 "cp -f /usr/share/gnu-config/config.sub .",
                                 "cp -f /usr/share/gnu-config/config.guess ."
                             ]
@@ -100,155 +108,155 @@
                     ]
                 },
                 {
-                    "name": "libtirpc",
-                    "config-opts": [
+                    "name" : "libtirpc",
+                    "config-opts" : [
                         "--disable-gssapi",
                         "--libdir=/app/lib"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://downloads.sourceforge.net/sourceforge/libtirpc/libtirpc-1.3.2.tar.bz2";,
-                            "sha256": "e24eb88b8ce7db3b7ca6eb80115dd1284abc5ec32a8deccfed2224fc2532b9fd"
+                            "type" : "archive",
+                            "url" : 
"https://downloads.sourceforge.net/sourceforge/libtirpc/libtirpc-1.3.2.tar.bz2";,
+                            "sha256" : "e24eb88b8ce7db3b7ca6eb80115dd1284abc5ec32a8deccfed2224fc2532b9fd"
                         }
                     ]
                 },
                 {
-                    "name": "rpcsvc-proto",
-                    "buildsystem": "autotools",
-                    "sources": [
+                    "name" : "rpcsvc-proto",
+                    "buildsystem" : "autotools",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://github.com/thkukuk/rpcsvc-proto/archive/v1.4.1.tar.gz";,
-                            "sha256": "750f7e57b81407a25b707867e90d7ee80aeb53bf515b114fc218f3c78dc9a6e8"
+                            "type" : "archive",
+                            "url" : "https://github.com/thkukuk/rpcsvc-proto/archive/v1.4.1.tar.gz";,
+                            "sha256" : "750f7e57b81407a25b707867e90d7ee80aeb53bf515b114fc218f3c78dc9a6e8"
                         }
                     ]
                 },
                 {
-                    "name": "docutils",
-                    "buildsystem": "simple",
-                    "build-commands": [
+                    "name" : "docutils",
+                    "buildsystem" : "simple",
+                    "build-commands" : [
                         "pip3 install --prefix=/app docutils-0.16-py2.py3-none-any.whl"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "file",
-                            "url": 
"https://files.pythonhosted.org/packages/81/44/8a15e45ffa96e6cf82956dd8d7af9e666357e16b0d93b253903475ee947f/docutils-0.16-py2.py3-none-any.whl";,
-                            "sha256": "0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"
+                            "type" : "file",
+                            "url" : 
"https://files.pythonhosted.org/packages/81/44/8a15e45ffa96e6cf82956dd8d7af9e666357e16b0d93b253903475ee947f/docutils-0.16-py2.py3-none-any.whl";,
+                            "sha256" : "0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"
                         }
                     ]
                 },
                 {
-                    "name": "libvirt",
-                    "builddir": true,
-                    "buildsystem": "meson",
-                    "build-options": {
-                        "cflags": "-I/app/include/tirpc -ltirpc"
+                    "name" : "libvirt",
+                    "builddir" : true,
+                    "buildsystem" : "meson",
+                    "build-options" : {
+                        "cflags" : "-I/app/include/tirpc -ltirpc"
                     },
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://libvirt.org/sources/libvirt-7.7.0.tar.xz";,
-                            "sha256": "1b616099c18d14b9424a622f2a0bd3e0cfa286414f3416bd1a8173621b2252b2"
+                            "type" : "archive",
+                            "url" : "https://libvirt.org/sources/libvirt-7.7.0.tar.xz";,
+                            "sha256" : "1b616099c18d14b9424a622f2a0bd3e0cfa286414f3416bd1a8173621b2252b2"
                         },
                         {
-                            "type": "patch",
-                            "path": "libvirt-use-monitor-in-xdg-runtime-dir.patch"
+                            "type" : "patch",
+                            "path" : "libvirt-use-monitor-in-xdg-runtime-dir.patch"
                         }
                     ]
                 }
             ]
         },
         {
-            "name": "spice-gtk",
-            "buildsystem": "meson",
-            "build-options": {
-                "env": {
-                    "PYTHONPATH": "/app"
+            "name" : "spice-gtk",
+            "buildsystem" : "meson",
+            "build-options" : {
+                "env" : {
+                    "PYTHONPATH" : "/app"
                 }
             },
-            "config-opts": [
+            "config-opts" : [
                 "-Dvapi=enabled",
                 "-Dwebdav=enabled",
                 "-Dgtk_doc=disabled"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "archive",
-                    "url": "https://www.spice-space.org/download/gtk/spice-gtk-0.39.tar.xz";,
-                    "sha256": "23acbee197eaaec9bce6e6bfd885bd8f79708332639243ff04833020865713cd"
+                    "type" : "archive",
+                    "url" : "https://www.spice-space.org/download/gtk/spice-gtk-0.39.tar.xz";,
+                    "sha256" : "23acbee197eaaec9bce6e6bfd885bd8f79708332639243ff04833020865713cd"
                 }
             ],
-            "modules": [
+            "modules" : [
                 {
-                    "name": "spice-protocol",
-                    "buildsystem": "meson",
-                    "sources": [
+                    "name" : "spice-protocol",
+                    "buildsystem" : "meson",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://www.spice-space.org/download/releases/spice-protocol-0.14.3.tar.xz";,
-                            "sha256": "f986e5bc2a1598532c4897f889afb0df9257ac21c160c083703ae7c8de99487a"
+                            "type" : "archive",
+                            "url" : 
"https://www.spice-space.org/download/releases/spice-protocol-0.14.3.tar.xz";,
+                            "sha256" : "f986e5bc2a1598532c4897f889afb0df9257ac21c160c083703ae7c8de99487a"
                         }
                     ]
                 },
                 {
-                    "name": "phodav",
-                    "buildsystem": "meson",
-                    "config-opts": [
+                    "name" : "phodav",
+                    "buildsystem" : "meson",
+                    "config-opts" : [
                         "-Dsystemd=disabled"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://ftp.gnome.org/pub/GNOME/sources/phodav/2.5/phodav-2.5.tar.xz";,
-                            "sha256": "71f0a9cd70afd4dd1412a0298331dbb8ac71c0377f52117afc15eb88dc6fb910"
+                            "type" : "archive",
+                            "url" : "https://ftp.gnome.org/pub/GNOME/sources/phodav/2.5/phodav-2.5.tar.xz";,
+                            "sha256" : "71f0a9cd70afd4dd1412a0298331dbb8ac71c0377f52117afc15eb88dc6fb910"
                         }
                     ]
                 },
                 {
-                    "name": "python-pyparsing",
-                    "buildsystem": "simple",
-                    "build-commands": [
+                    "name" : "python-pyparsing",
+                    "buildsystem" : "simple",
+                    "build-commands" : [
                         "pip3 install --prefix=/app pyparsing-2.4.6-py2.py3-none-any.whl"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "file",
-                            "url": 
"https://files.pythonhosted.org/packages/5d/bc/1e58593167fade7b544bfe9502a26dc860940a79ab306e651e7f13be68c2/pyparsing-2.4.6-py2.py3-none-any.whl";,
-                            "sha256": "c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
+                            "type" : "file",
+                            "url" : 
"https://files.pythonhosted.org/packages/5d/bc/1e58593167fade7b544bfe9502a26dc860940a79ab306e651e7f13be68c2/pyparsing-2.4.6-py2.py3-none-any.whl";,
+                            "sha256" : "c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
                         }
                     ]
                 },
                 {
-                    "name": "libcacard",
-                    "sources": [
+                    "name" : "libcacard",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://www.spice-space.org/download/libcacard/libcacard-2.8.1.tar.xz";,
-                            "sha256": "fbbf4de8cb7db5bdff5ecb672ff0dbe6939fb9f344b900d51ba6295329a332e7"
+                            "type" : "archive",
+                            "url" : "https://www.spice-space.org/download/libcacard/libcacard-2.8.1.tar.xz";,
+                            "sha256" : "fbbf4de8cb7db5bdff5ecb672ff0dbe6939fb9f344b900d51ba6295329a332e7"
                         }
                     ]
                 },
                 {
-                    "name": "spice",
-                    "buildsystem": "meson",
-                    "config-opts": [
+                    "name" : "spice",
+                    "buildsystem" : "meson",
+                    "config-opts" : [
                         "-Dlz4=false",
                         "-Dmanual=false"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://spice-space.org/download/releases/spice-0.14.3.tar.bz2";,
-                            "sha256": "551d4be4a07667cf0543f3c895beb6da8a93ef5a9829f2ae47817be5e616a114"
+                            "type" : "archive",
+                            "url" : "https://spice-space.org/download/releases/spice-0.14.3.tar.bz2";,
+                            "sha256" : "551d4be4a07667cf0543f3c895beb6da8a93ef5a9829f2ae47817be5e616a114"
                         }
                     ]
                 }
             ]
         },
         {
-            "name": "qemu",
-            "config-opts": [
+            "name" : "qemu",
+            "config-opts" : [
                 "--disable-user",
                 "--enable-kvm",
                 "--enable-spice",
@@ -259,147 +267,147 @@
                 "--python=/bin/python3",
                 "--target-list=x86_64-softmmu,i386-softmmu"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "archive",
-                    "url": "https://download.qemu.org/qemu-6.1.0.tar.xz";,
-                    "sha256": "eebc089db3414bbeedf1e464beda0a7515aad30f73261abc246c9b27503a3c96"
+                    "type" : "archive",
+                    "url" : "https://download.qemu.org/qemu-6.1.0.tar.xz";,
+                    "sha256" : "eebc089db3414bbeedf1e464beda0a7515aad30f73261abc246c9b27503a3c96"
                 }
             ],
-            "modules": [
+            "modules" : [
                 {
-                    "name": "libusb",
-                    "config-opts": [
+                    "name" : "libusb",
+                    "config-opts" : [
                         "--disable-udev"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://github.com/libusb/libusb/releases/download/v1.0.24/libusb-1.0.24.tar.bz2";,
-                            "sha256": "7efd2685f7b327326dcfb85cee426d9b871fd70e22caa15bb68d595ce2a2b12a"
+                            "type" : "archive",
+                            "url" : 
"https://github.com/libusb/libusb/releases/download/v1.0.24/libusb-1.0.24.tar.bz2";,
+                            "sha256" : "7efd2685f7b327326dcfb85cee426d9b871fd70e22caa15bb68d595ce2a2b12a"
                         }
                     ]
                 },
                 {
-                    "name": "usbredir",
+                    "name" : "usbredir",
                     "buildsystem" : "meson",
-                    "config-opts": [
+                    "config-opts" : [
                         "-Dtools=disabled"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://spice-space.org/download/usbredir/usbredir-0.11.0.tar.xz";,
-                            "sha256": "72dd5f3aa90dfbc0510b5149bb5b1654c8f21fdc405dfce7b5dc163dcff19cba"
+                            "type" : "archive",
+                            "url" : "https://spice-space.org/download/usbredir/usbredir-0.11.0.tar.xz";,
+                            "sha256" : "72dd5f3aa90dfbc0510b5149bb5b1654c8f21fdc405dfce7b5dc163dcff19cba"
                         }
                     ]
                 },
                 {
-                    "name": "virglrenderer",
-                    "buildsystem": "meson",
-                    "sources": [
+                    "name" : "virglrenderer",
+                    "buildsystem" : "meson",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": 
"https://github.com/freedesktop/virglrenderer/archive/virglrenderer-0.8.2.tar.gz";,
-                            "sha256": "9fa93095cd9a3e5b13c740e5e3b656a989356732bdaf3e22acb7c38a1f1f4411"
+                            "type" : "archive",
+                            "url" : 
"https://github.com/freedesktop/virglrenderer/archive/virglrenderer-0.8.2.tar.gz";,
+                            "sha256" : "9fa93095cd9a3e5b13c740e5e3b656a989356732bdaf3e22acb7c38a1f1f4411"
                         }
                     ]
                 }
             ]
         },
         {
-            "name": "gtk-vnc",
-            "buildsystem": "meson",
-            "config-opts": [
+            "name" : "gtk-vnc",
+            "buildsystem" : "meson",
+            "config-opts" : [
                 "-Dpulseaudio=enabled"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "archive",
-                    "url": "https://download.gnome.org/sources/gtk-vnc/1.2/gtk-vnc-1.2.0.tar.xz";,
-                    "sha256": "7aaf80040d47134a963742fb6c94e970fcb6bf52dc975d7ae542b2ef5f34b94a"
+                    "type" : "archive",
+                    "url" : "https://download.gnome.org/sources/gtk-vnc/1.2/gtk-vnc-1.2.0.tar.xz";,
+                    "sha256" : "7aaf80040d47134a963742fb6c94e970fcb6bf52dc975d7ae542b2ef5f34b94a"
                 }
             ]
         },
         {
-            "name": "libosinfo",
-            "buildsystem": "meson",
-            "config-opts": [
+            "name" : "libosinfo",
+            "buildsystem" : "meson",
+            "config-opts" : [
                 "-Dwith-pci-ids-path=/app/share/hwdata/pci.ids",
                 "-Dwith-usb-ids-path=/app/share/hwdata/usb.ids"
             ],
-            "post-install": [
+            "post-install" : [
                 "install -Dm644 ../pci.ids /app/share/hwdata/pci.ids",
                 "install -Dm644 ../usb.ids /app/share/hwdata/usb.ids"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "archive",
-                    "url": "https://releases.pagure.org/libosinfo/libosinfo-1.9.0.tar.xz";,
-                    "sha256": "b4f3418154ef3f43d9420827294916aea1827021afc06e1644fc56951830a359"
+                    "type" : "archive",
+                    "url" : "https://releases.pagure.org/libosinfo/libosinfo-1.9.0.tar.xz";,
+                    "sha256" : "b4f3418154ef3f43d9420827294916aea1827021afc06e1644fc56951830a359"
                 },
                 {
-                    "type": "file",
-                    "path": "pci.ids"
+                    "type" : "file",
+                    "path" : "pci.ids"
                 },
                 {
-                    "type": "file",
-                    "path": "usb.ids"
+                    "type" : "file",
+                    "path" : "usb.ids"
                 }
             ],
-            "modules": [
+            "modules" : [
                 {
-                    "name": "cdrkit",
-                    "buildsystem": "cmake",
-                    "sources": [
+                    "name" : "cdrkit",
+                    "buildsystem" : "cmake",
+                    "sources" : [
                         {
-                            "type": "git",
-                            "url": "https://github.com/Distrotech/cdrkit";,
-                            "branch": "7b4bb72389ea5ea3ecc94545036dcff4728ec38a"
+                            "type" : "git",
+                            "url" : "https://github.com/Distrotech/cdrkit";,
+                            "branch" : "7b4bb72389ea5ea3ecc94545036dcff4728ec38a"
                         },
                         {
-                            "type": "patch",
-                            "path": "genisoimage-extern.patch"
+                            "type" : "patch",
+                            "path" : "genisoimage-extern.patch"
                         }
                     ]
                 },
                 {
-                    "name": "osinfo-db-tools",
-                    "buildsystem": "meson",
-                    "sources": [
+                    "name" : "osinfo-db-tools",
+                    "buildsystem" : "meson",
+                    "sources" : [
                         {
-                            "type": "archive",
-                            "url": "https://releases.pagure.org/libosinfo/osinfo-db-tools-1.9.0.tar.xz";,
-                            "sha256": "255f1c878bacec70c3020ff5a9cb0f6bd861ca0009f24608df5ef6f62d5243c0"
+                            "type" : "archive",
+                            "url" : "https://releases.pagure.org/libosinfo/osinfo-db-tools-1.9.0.tar.xz";,
+                            "sha256" : "255f1c878bacec70c3020ff5a9cb0f6bd861ca0009f24608df5ef6f62d5243c0"
                         }
                     ]
                 },
                 {
-                    "name": "osinfo-db",
-                    "buildsystem": "simple",
-                    "builddir": true,
-                    "build-commands": [
+                    "name" : "osinfo-db",
+                    "buildsystem" : "simple",
+                    "builddir" : true,
+                    "build-commands" : [
                         "make",
                         "osinfo-db-import --dir /app/share/osinfo/ osinfo-db-*.tar.xz"
                     ],
-                    "sources": [
+                    "sources" : [
                         {
-                            "type": "git",
-                            "url": "https://gitlab.com/libosinfo/osinfo-db.git";
+                            "type" : "git",
+                            "url" : "https://gitlab.com/libosinfo/osinfo-db.git";
                         }
                     ]
                 }
             ]
         },
         {
-            "name": "tracker-miners",
-            "buildsystem": "meson",
-            "cleanup": [
+            "name" : "tracker-miners",
+            "buildsystem" : "meson",
+            "cleanup" : [
                 "/etc",
                 "/libexec",
                 "/share/tracker/miners"
             ],
-            "config-opts": [
+            "config-opts" : [
                 "-Ddomain_prefix=org.gnome.BoxesDevel",
                 "-Dextract=true",
                 "-Dgeneric_media_extractor=none",
@@ -410,42 +418,45 @@
                 "-Dsystemd_user_services=false",
                 "-Dwriteback=false"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "git",
-                    "url": "https://gitlab.gnome.org/GNOME/tracker-miners.git";
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/GNOME/tracker-miners.git";
                 }
             ]
         },
         {
-            "name": "govf",
-            "config-opts": [
+            "name" : "govf",
+            "config-opts" : [
                 "--libdir=/app/lib"
             ],
-            "buildsystem": "meson",
-            "sources": [
+            "buildsystem" : "meson",
+            "sources" : [
                 {
-                    "type": "git",
-                    "url": "https://gitlab.gnome.org/felipeborges/libovf-glib.git";
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/felipeborges/libovf-glib.git";
                 }
             ]
         },
         {
-            "name": "gnome-boxes",
-            "buildsystem": "meson",
-            "config-opts": [
+            "name" : "gnome-boxes",
+            "buildsystem" : "meson",
+            "config-opts" : [
                 "-Dflatpak=true",
                 "-Dprofile=development",
                 "-Ddistributor_name=gnome-boxes-nightly",
                 "-Ddistributor_version=master",
                 "-Drdp=false"
             ],
-            "sources": [
+            "sources" : [
                 {
-                    "type": "git",
-                    "url": "https://gitlab.gnome.org/gnome/gnome-boxes.git";
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/gnome/gnome-boxes.git";
                 }
             ]
         }
-    ]
+    ],
+    "build-options" : {
+        "env" : {        }
+    }
 }
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 5fe5d7ed..f5f4796c 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -21,7 +21,6 @@
     <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>
@@ -29,9 +28,6 @@
     <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 +48,16 @@
     <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/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/devices-page.ui b/data/ui/preferences/devices-page.ui
new file mode 100644
index 00000000..42187b2e
--- /dev/null
+++ b/data/ui/preferences/devices-page.ui
@@ -0,0 +1,108 @@
+<?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 &amp; Shares</property>
+
+    <child>
+      <object class="HdyPreferencesGroup" id="devices_group">
+        <property name="visible">True</property>
+        <property name="title" translatable="yes">Devices</property>
+      </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..a79159ea
--- /dev/null
+++ b/data/ui/preferences/resources-page.ui
@@ -0,0 +1,286 @@
+<?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" id="resources_group">
+        <property name="visible">True</property>
+
+        <child>
+          <object class="HdyActionRow">
+            <property name="visible">True</property>
+            <property name="title" translatable="yes">CPU</property>
+
+            <child>
+              <object class="GtkLevelBar" id="cpu_level_bar">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="halign">fill</property>
+                <property name="valign">center</property>
+                <property name="width-request">200</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <child>
+          <object class="HdyActionRow">
+            <property name="visible">True</property>
+            <property name="title" translatable="yes">I/O</property>
+
+            <child>
+              <object class="GtkLevelBar" id="io_level_bar">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="halign">fill</property>
+                <property name="valign">center</property>
+                <property name="width-request">200</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <child>
+          <object class="HdyActionRow">
+            <property name="visible">True</property>
+            <property name="title" translatable="yes">Network</property>
+
+            <child>
+              <object class="GtkLevelBar" id="net_level_bar">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="halign">fill</property>
+                <property name="valign">center</property>
+                <property name="width-request">200</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="output" handler="on_memory_spin_button_output"/>
+                <signal name="value-changed" handler="on_memory_spin_button_value_changed"/>
+              </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="input_purpose">number</property>
+                <property name="valign">center</property>
+              </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>
+
+        <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>
+
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="visible">True</property>
+
+        <child>
+          <object class="GtkBox" id="power_buttons_box">
+            <property name="visible">True</property>
+            <property name="halign">end</property>
+            <style>
+              <class name="linked"/>
+            </style>
+
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Restart</property>
+                <signal name="clicked" handler="on_restart_button_clicked"/>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Force Shutdown</property>
+                <signal name="clicked" handler="on_force_shutdown_button_clicked"/>
+                <style>
+                  <class name="destructive-action"/>
+                </style>
+              </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..04045122
--- /dev/null
+++ b/data/ui/preferences/shared-folders-widget.ui
@@ -0,0 +1,24 @@
+<?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="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>
+            <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..858da282
--- /dev/null
+++ b/data/ui/preferences/snapshots-page.ui
@@ -0,0 +1,59 @@
+<?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">
+        <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="snapshot_list">
+                <property name="visible">True</property>
+                <signal name="add" handler="update_snapshot_stack_page"/>
+                <signal name="remove" handler="update_snapshot_stack_page"/>
+              </object>
+            </child>
+
+            <child>
+              <object class="HdyStatusPage" id="empty_page">
+                <property name="visible">True</property>
+                <property name="title" translatable="yes">No Snapshots Created Yet</property>
+                <property name="description" translatable="yes">Create one using the button 
bellow.</property>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkBox" id="activity_box">
+                <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/src/actions-popover.vala b/src/actions-popover.vala
index c953532f..83db7a1a 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,12 @@ 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
+        };
+        preferences.present ();
     }
 }
diff --git a/src/app-window.vala b/src/app-window.vala
index 7ee13cbc..fc4a4523 100644
--- a/src/app-window.vala
+++ b/src/app-window.vala
@@ -295,28 +295,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..74289680 100644
--- a/src/libvirt-machine-properties.vala
+++ b/src/libvirt-machine-properties.vala
@@ -148,7 +148,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);
             }
@@ -318,9 +319,9 @@ private void add_resource_usage_graphs (ref List<Boxes.Property> list) {
         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;
+            //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);
@@ -781,13 +782,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..5779447c 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -371,9 +371,9 @@ private async void update_net_stat (DomainInfo info, MachineStat *stat) {
 
     public signal void stats_updated ();
 
-    public double[] cpu_stats;
-    public double[] io_stats;
-    public double[] net_stats;
+    public double cpu_stats;
+    public double io_stats;
+    public double net_stats;
     private async void update_stats () {
         try {
             var now = get_monotonic_time ();
@@ -394,14 +394,10 @@ private async void update_stats () {
             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);
+            cpu_stats = stat.cpu_guest_percent;
+            net_stats = (stat.net_read + stat.net_write);
+            io_stats = (stat.disk_read + stat.disk_write);
         }
 
         stats_updated ();
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..4909c551 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -109,11 +109,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,
diff --git a/src/preferences/devices-page.vala b/src/preferences/devices-page.vala
new file mode 100644
index 00000000..d94378f9
--- /dev/null
+++ b/src/preferences/devices-page.vala
@@ -0,0 +1,54 @@
+// 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 Hdy.PreferencesGroup devices_group;
+    [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;
+
+        devices_group.visible = !App.is_running_in_flatpak ();
+
+        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_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..cd821f5d
--- /dev/null
+++ b/src/preferences/meson.build
@@ -0,0 +1,8 @@
+vala_sources += files( 
+  'devices-page.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..98c5d848
--- /dev/null
+++ b/src/preferences/resources-page.vala
@@ -0,0 +1,258 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/preferences/resources-page.ui")]
+private class Boxes.ResourcesPage : Hdy.PreferencesPage {
+    private LibvirtMachine machine;
+
+    private string logs;
+
+    [GtkChild]
+    private unowned Gtk.Entry box_name_entry;
+
+    [GtkChild]
+    private unowned Hdy.PreferencesGroup resources_group;
+    [GtkChild]
+    private unowned Gtk.LevelBar cpu_level_bar;
+    [GtkChild]
+    private unowned Gtk.LevelBar io_level_bar;
+    [GtkChild]
+    private unowned Gtk.LevelBar net_level_bar;
+
+    [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;
+    [GtkChild]
+    private unowned Gtk.Box power_buttons_box;
+
+    public async void setup (LibvirtMachine machine) {
+        this.machine = machine;
+
+        bind_widget_property (box_name_entry, "text", "name");
+
+        machine.notify["state"].connect (on_machine_state_change);
+        on_machine_state_change ();
+
+        update_recommended_resource_labels ();
+
+        bind_widget_property (run_in_bg_toggle, "active", "run-in-bg");
+
+        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 ();
+
+            // RAM Memory
+            var ram = machine.domain_config.memory * Osinfo.KIBIBYTES;
+            var max_ram = host_topology.memory * Osinfo.KIBIBYTES;
+
+            memory_spin_button.set_range (64 * Osinfo.MEBIBYTES, max_ram);
+            memory_spin_button.set_increments (64* Osinfo.MEBIBYTES, 64 * Osinfo.MEBIBYTES);
+            memory_spin_button.set_value (ram);
+
+            // CPUs
+            var cpus = (int)machine.domain_config.get_vcpus ();
+            var max_cpus = host_topology.cores * host_topology.sockets * host_topology.threads;
+
+            cpus_spin_button.set_range (1, max_cpus);
+            cpus_spin_button.set_increments (1, max_cpus);
+            cpus_spin_button.set_value (cpus);
+            
+        } catch (GLib.Error error) {
+            warning ("Failed to obtain virtual resources for machine: %s", error.message);
+        }
+    }
+
+    private async void update_recommended_resource_labels () {
+        var os = yield get_os_for_machine (machine);
+        if (os == null)
+            return;
+
+        var architecture = machine.domain_config.get_os ().get_arch ();
+        var resources = OSDatabase.get_recommended_resources_for_os (os, architecture);
+        if (resources != null) {
+            var row_subtitle = _("Recommended %s");
+
+            memory_row.set_subtitle (row_subtitle.printf (GLib.format_size (resources.ram,
+                                                                            
GLib.FormatSizeFlags.IEC_UNITS)));
+            storage_row.set_subtitle (row_subtitle.printf (GLib.format_size (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 on_machine_state_change () {
+        cpu_level_bar.value = machine.cpu_stats;
+        io_level_bar.value = machine.io_stats;
+        net_level_bar.value = machine.net_stats;
+
+        resources_group.visible = machine.is_running;
+        power_buttons_box.visible = machine.is_running;
+    }
+
+    [GtkCallback]
+    private void on_memory_spin_button_value_changed () {
+        // needs_reboot.set_reveal_child (value != machine.ram);
+    }
+
+    [GtkCallback]
+    private bool on_memory_spin_button_output (Gtk.SpinButton spin_button) {
+        var adjustment = spin_button.get_adjustment ();
+        var ram = (uint64)adjustment.get_value ();
+
+        spin_button.set_text (GLib.format_size (ram, GLib.FormatSizeFlags.IEC_UNITS));
+
+        return true;
+    }
+
+    [GtkCallback]
+    private void on_troubleshooting_logs_button_clicked () {
+        if (logs == null)
+            logs = collect_logs ();
+        
+        try {
+            FileIOStream io_stream;
+            File file = File.new_tmp (null, out io_stream);
+
+            OutputStream ostream = io_stream.output_stream;
+            DataOutputStream do_stream = new DataOutputStream (ostream);
+            do_stream.put_string (logs);
+
+            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);
+        }
+    }
+
+    private string collect_logs () {
+        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;
+    }
+
+    [GtkCallback]
+    private void on_edit_configuration_button_clicked () {
+
+        /*
+         * New configuration management system:
+         *
+         * 1. If you ever edit a VM -> we store your edit in an $XDG_CONFIG/../boxes-name.xml
+         * 2. If the file exists, it overrides Boxes' VMConfigurator updates.
+         * 3. If the user clicks "Reset", we delete the file and the VMConfigurator updates take place.
+         *
+         * This gets rid of the "<edited>" tag thing.
+         * Allows for opening the file with a Portal
+         */
+
+        var domain_xml = machine.domain_config.to_xml ();
+        
+        // FIXME: let's fix these tags formats
+        /*string metadata = "<edited>%u</edited>".printf (0);
+        try {
+            machine.domain_config.set_custom_xml (metadata, "edited", BOXES_NS_URI);
+        } catch (GLib.Error error) {
+            warning ("Failed to attach metadata to domain config: %s", error.message);
+        }*/
+
+        // write it down to a file accessible outside the sandbox.
+        // monitor file for changes.
+        // Apply changes.
+        // Show errors accordingly.
+    }
+
+    [GtkCallback]
+    private void on_force_shutdown_button_clicked () {
+        machine.force_shutdown ();
+    }
+
+    [GtkCallback]
+    private void on_restart_button_clicked () {
+        machine.restart ();
+    }
+
+    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);
+    }
+}
diff --git a/src/preferences/shared-folders-widget.vala b/src/preferences/shared-folders-widget.vala
new file mode 100644
index 00000000..8d5340d2
--- /dev/null
+++ b/src/preferences/shared-folders-widget.vala
@@ -0,0 +1,109 @@
+// 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;
+    //[GtkChild]
+    //private unowned Gtk.MenuButton add_button;
+
+    construct {
+        popover = new SharedFolderPopover ();
+        popover.saved.connect (on_popover_saved);
+        //add_button.popover = popover;
+    }
+
+    public void setup (string machine_uuid) {
+        this.machine_uuid = machine_uuid;
+
+        list_model = manager.get_folders (machine_uuid);
+        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);
+    }
+
+    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;
+    }
+}
+
+[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 98%
rename from src/snapshot-list-row.vala
rename to src/preferences/snapshot-list-row.vala
index dee9b007..2602af2e 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 = ""; }
diff --git a/src/preferences/snapshots-page.vala b/src/preferences/snapshots-page.vala
new file mode 100644
index 00000000..e1ab0345
--- /dev/null
+++ b/src/preferences/snapshots-page.vala
@@ -0,0 +1,111 @@
+// 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 Gtk.Stack stack;
+    [GtkChild]
+    private unowned Gtk.ListBox snapshot_list;
+    [GtkChild]
+    private unowned Hdy.StatusPage empty_page;
+    [GtkChild]
+    private unowned Gtk.Box activity_box;
+    [GtkChild]
+    private unowned Gtk.Label activity_label;
+
+    private string? activity {
+        set {
+            if (value == null) {
+                stack.visible_child = snapshot_list;
+            } else {
+                activity_label.label = value;
+                stack.visible_child = activity_box;
+            }
+        }
+    }
+
+    public void setup (LibvirtMachine machine) {
+        this.machine = machine;
+
+        snapshot_list.set_sort_func (config_sort_func);
+
+        //fetch_snapshots.begin (machine);
+
+        var add_button = new Gtk.Button () {
+            visible = true,
+            image = new Gtk.Image () {
+                visible = true,
+                icon_name = "list-add-symbolic"
+            }
+        };
+        snapshot_list.add (add_button);
+        add_button.clicked.connect (create_snapshot);
+    }
+
+    private async void fetch_snapshots (LibvirtMachine machine) {
+        try {
+            var snapshots = yield machine.properties.get_snapshots (null);
+
+            foreach (var snapshot in snapshots) {
+                var row = new SnapshotListRow (snapshot, machine);
+                row.notify["activity-message"].connect (row_activity_changed);
+                snapshot_list.add (row);
+            }
+        } catch (GLib.Error e) {
+            warning ("Could not fetch snapshots: %s", e.message);
+        }
+
+        update_snapshot_stack_page ();
+    }
+
+    private int config_sort_func (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
+        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 ();
+            var new_row = new SnapshotListRow (new_snapshot, machine);
+            new_row.notify["activity-message"].connect (row_activity_changed);
+            snapshot_list.add (new_row);
+        } 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;
+    }
+
+    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_snapshots = snapshot_list.get_children ().length ();
+        if (num_snapshots > 1)
+            stack.visible_child = snapshot_list;
+        else
+            stack.visible_child = empty_page;
+    }
+}
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..c39aaf23 100644
--- a/src/spice-display.vala
+++ b/src/spice-display.vala
@@ -412,9 +412,6 @@ private void on_new_file_transfer (Spice.MainChannel main_channel, Object transf
             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;
         }
 
@@ -523,10 +520,6 @@ public override void send_keys (uint[] keyvals) {
         return frame;
     }
 
-    private Gtk.Frame create_shared_folders_frame () {
-        return new SharedFoldersWidget (machine.config.uuid);
-    }
-
     private bool is_usb_kbd_or_mouse (uint8 class, uint8 subclass, uint8 protocol) {
         var ret = false;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]