[gnome-boxes] Add SnapshotListRow
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes] Add SnapshotListRow
- Date: Fri, 29 Aug 2014 16:14:35 +0000 (UTC)
commit bfb4c17ab46a26935d11752729dbf189393c8ff0
Author: Timm Bäder <mail baedert org>
Date: Mon Jul 28 10:33:23 2014 +0200
Add SnapshotListRow
This will be used in the snapshots page of the VM properties and
features a way to delete, rename or revert to the snapshot it
represents.
https://bugzilla.gnome.org/show_bug.cgi?id=710306
data/gnome-boxes.gresource.xml | 1 +
data/gtk-style.css | 13 +++
data/ui/snapshot-list-row.ui | 113 +++++++++++++++++++
src/Makefile.am | 1 +
src/snapshot-list-row.vala | 231 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 359 insertions(+), 0 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 14ae7ea..87b856e 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -28,5 +28,6 @@
<file preprocess="xml-stripblanks">ui/wizard-source.ui</file>
<file preprocess="xml-stripblanks">ui/wizard-summary.ui</file>
<file preprocess="xml-stripblanks">ui/wizard-toolbar.ui</file>
+ <file preprocess="xml-stripblanks">ui/snapshot-list-row.ui</file>
</gresource>
</gresources>
diff --git a/data/gtk-style.css b/data/gtk-style.css
index be6e374..0eab77e 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -12,6 +12,19 @@
-GtkWidget-separator-height: 1;
}
+.boxes-snapshot-list-row {
+ border-bottom: 1px solid @borders;
+}
+
+.boxes-snapshot-list-row.indicator {
+ background-color: @insensitive_fg_color;
+ border: none;
+}
+
+.boxes-snapshot-list-row.indicator.active {
+ background-color: @theme_fg_color;
+}
+
BoxesMiniGraph {
background-color: @content_view_bg;
border-color: @borders;
diff --git a/data/ui/snapshot-list-row.ui b/data/ui/snapshot-list-row.ui
new file mode 100644
index 0000000..f20ee62
--- /dev/null
+++ b/data/ui/snapshot-list-row.ui
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <menu id="actions_menu">
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Revert to this state</attribute>
+ <attribute name="action">snap.revert-to</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Rename</attribute>
+ <attribute name="action">snap.rename</attribute>
+ </item>
+ </section>
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Delete</attribute>
+ <attribute name="action">snap.delete</attribute>
+ </item>
+ </section>
+ </menu>
+
+ <template class="BoxesSnapshotListRow" parent="GtkListBoxRow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkRevealer" id="delete_revealer">
+ <property name="visible">true</property>
+ <property name="can_focus">false</property>
+ <property name="reveal_child">true</property>
+
+ <child>
+ <object class="GtkStack" id="mode_stack">
+ <property name="visible">true</property>
+ <property name="can_focus">false</property>
+ <property name="transition_type">slide-up-down</property>
+ <child>
+ <object class="GtkBox" id="show_name_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="margin_start">45</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkMenuButton" id="more_button">
+ <property name="visible">true</property>
+ <property name="menu_model">actions_menu</property>
+ <property name="valign">center</property>
+ <property name="margin_top">4</property>
+ <property name="margin_bottom">4</property>
+ <property name="margin_end">4</property>
+ <style>
+ <class name="image-button" />
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">true</property>
+ <property name="icon-name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkBox" id="edit_name_box">
+ <property name="visible">true</property>
+ <property name="orientation">horizontal</property>
+ <property name="can_focus">false</property>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="visible">true</property>
+ <property name="can_focus">true</property>
+ <property name="margin_start">45</property>
+ <property name="hexpand">true</property>
+ <signal name="activate" handler="on_save_name_button_clicked" />
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="save_name_button">
+ <property name="can_focus">true</property>
+ <property name="visible">true</property>
+ <property name="margin_start">10</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Done</property>
+ <signal name="clicked" handler="on_save_name_button_clicked" />
+ <style>
+ <class name="text-button" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 9ae8dbb..bc673bb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -149,6 +149,7 @@ gnome_boxes_SOURCES = \
downloader.vala \
empty-boxes.vala \
tracker-iso-query.vala \
+ snapshot-list-row.vala \
$(NULL)
gnome_boxes_LDADD = \
diff --git a/src/snapshot-list-row.vala b/src/snapshot-list-row.vala
new file mode 100644
index 0000000..bbe3011
--- /dev/null
+++ b/src/snapshot-list-row.vala
@@ -0,0 +1,231 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/snapshot-list-row.ui")]
+private class Boxes.SnapshotListRow : Gtk.ListBoxRow {
+ public GVir.DomainSnapshot snapshot;
+ public string activity_message { get; set; default = ""; }
+
+ [GtkChild]
+ private Gtk.Revealer delete_revealer;
+ [GtkChild]
+ private Gtk.Label name_label;
+ [GtkChild]
+ private Gtk.Stack mode_stack;
+ [GtkChild]
+ private Gtk.Entry name_entry;
+ [GtkChild]
+ private Gtk.Box edit_name_box;
+ [GtkChild]
+ private Gtk.Box show_name_box;
+
+ // index of the snapshot in the list
+ private int index;
+ private int parent_size;
+
+ private Boxes.LibvirtMachine machine;
+ private unowned Gtk.Container? parent_container = null;
+
+ private const GLib.ActionEntry[] action_entries = {
+ {"revert-to", revert_to_activated},
+ {"rename", rename_activated},
+ {"delete", delete_activated}
+ };
+
+ construct {
+ this.get_style_context ().add_class ("boxes-snapshot-list-row");
+ this.parent_set.connect (() => {
+ var parent = get_parent () as Gtk.Container;
+
+ if (parent == null)
+ return;
+
+ this.parent_container = parent;
+ update_index ();
+ parent.add.connect (update_index);
+ parent.remove.connect (update_index);
+ });
+ this.selectable = false;
+ this.activatable = false;
+ }
+
+
+ public SnapshotListRow (GVir.DomainSnapshot snapshot,
+ LibvirtMachine machine) {
+ this.snapshot = snapshot;
+ this.machine = machine;
+
+ try {
+ name_label.label = snapshot.get_config (0).get_description ();
+ } catch (GLib.Error e) {
+ critical (e.message);
+ }
+
+ var action_group = new GLib.SimpleActionGroup ();
+ action_group.add_action_entries (action_entries, this);
+ this.insert_action_group ("snap", action_group);
+
+ delete_revealer.notify["child-revealed"].connect (() => {
+ if (!delete_revealer.child_revealed) {
+ parent_container = (Gtk.Container) this.get_parent ();
+ if (parent_container != null)
+ parent_container.remove (this);
+ }
+ });
+ }
+
+ // Need to override this in order to connect the indicators without any gaps.
+ public override bool draw (Cairo.Context ct) {
+ base.draw (ct);
+ var height = this.get_allocated_height ();
+ var sc = this.get_style_context ();
+
+ double indicator_size = height / 2.0;
+
+ sc.save ();
+ sc.add_class ("indicator");
+
+ var line_color = sc.get_background_color (this.get_state_flags ());
+ ct.set_source_rgba (line_color.red, line_color.green, line_color.blue, line_color.alpha);
+ ct.set_line_width (4);
+ if (index > 0) {
+ ct.move_to (height / 2.0 + 0.5, -1);
+ ct.line_to (height / 2.0 + 0.5, height / 2.0);
+ ct.stroke ();
+ }
+ if (index < parent_size - 1) {
+ ct.move_to (height / 2.0 + 0.5, height / 2.0);
+ ct.line_to (height / 2.0 + 0.5, height + 1);
+ ct.stroke ();
+ }
+
+ bool is_current = false;
+ try {
+ this.snapshot.get_is_current (0, out is_current);
+ } catch (GLib.Error e) {
+ warning (e.message);
+ }
+
+ if (is_current)
+ sc.add_class ("active");
+
+ ct.save();
+ sc.render_background (ct, height / 4.0, height / 4.0, indicator_size, indicator_size);
+ sc.render_frame (ct, height / 4.0, height / 4.0, indicator_size, indicator_size + 0.5);
+ ct.restore();
+
+ sc.restore ();
+
+ return true;
+ }
+
+ [GtkCallback]
+ private void on_save_name_button_clicked () {
+ try {
+ var config = snapshot.get_config (0);
+ config.set_description (name_entry.text);
+ snapshot.set_config (config);
+ name_label.label = name_entry.get_text ();
+ mode_stack.visible_child = show_name_box;
+ } catch (GLib.Error e) {
+ warning (e.message);
+ }
+ }
+
+
+ private void revert_to_activated (GLib.SimpleAction action, GLib.Variant? v) {
+ var snapshot_name = this.snapshot.get_name ();
+ var snapshot_state = GVirConfig.DomainSnapshotDomainState.NOSTATE;
+
+ try {
+ var snapshot_config = snapshot.get_config (0);
+ snapshot_name = snapshot_config.get_description ();
+ snapshot_state = snapshot_config.get_state ();
+ } catch (GLib.Error e) {}
+
+ var show_activity = (machine.state == Machine.MachineState.RUNNING);
+ if (show_activity)
+ activity_message = _("Reverting to %s...").printf (snapshot_name);
+
+ if (snapshot_state == GVirConfig.DomainSnapshotDomainState.SHUTOFF) {
+ ulong restart_id = 0;
+ restart_id = machine.domain.stopped.connect (() => {
+ machine.start.begin (Machine.ConnectFlags.NONE, null);
+ machine.domain.disconnect (restart_id);
+ });
+ }
+
+ snapshot.revert_to_async.begin (0, null, (obj, res) => {
+ try {
+ snapshot.revert_to_async.end (res);
+ parent_container.queue_draw ();
+ } catch (GLib.Error e) {
+ warning (e.message);
+ machine.window.notificationbar.display_error (_("Failed to apply snapshot"));
+ }
+ if (show_activity)
+ activity_message = "";
+ });
+ }
+
+
+ private void delete_activated (GLib.SimpleAction action, GLib.Variant? v) {
+ string snapshot_identifier = snapshot.get_name ();
+ try {
+ var config = snapshot.get_config (0);
+ snapshot_identifier = config.get_description ();
+ } catch (GLib.Error e) {
+ warning ("Could not get configuration of snapshot %s: %s",
+ snapshot.get_name (),
+ e.message);
+ }
+ var message = _("Snapshot \"%s\" deleted.").printf (snapshot_identifier);
+ delete_revealer.reveal_child = false;
+
+ Notification.OKFunc undo = () => {
+ parent_container.add (this);
+ this.visible = true;
+ delete_revealer.reveal_child = true;
+ };
+
+ Notification.CancelFunc really_remove = () => {
+ this.snapshot.delete_async.begin (0, null, (obj, res) =>{
+ try {
+ this.snapshot.delete_async.end (res);
+ parent_container.queue_draw ();
+ } catch (GLib.Error e) {
+ warning ("Error while deleting snapshot %s: %s", snapshot.get_name (), e.message);
+ }
+ });
+ };
+ machine.window.notificationbar.display_for_action (message,
+ _("_Undo"),
+ (owned) undo,
+ (owned) really_remove);
+ }
+
+ private void rename_activated (GLib.SimpleAction action, GLib.Variant? v) {
+ name_entry.text = name_label.get_text ();
+ mode_stack.visible_child = edit_name_box;
+ name_entry.grab_focus ();
+ }
+
+ public void unreveal () {
+ delete_revealer.reveal_child = false;
+ }
+
+ public void reveal () {
+ delete_revealer.reveal_child = true;
+ }
+
+
+ private void update_index () {
+ var parent = this.get_parent ();
+
+ if (parent == null || !(parent is Gtk.ListBox))
+ return;
+
+ var siblings = (parent as Gtk.Container).get_children ();
+ this.index = siblings.index (this);
+ this.parent_size = (int) siblings.length ();
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]