[baobab/import-export: 5/5] Add ability to save and load reports in JSON format



commit 17d5ce198f69bf4c3b555754044fd74c6c4f80f8
Author: Stefano Facchini <stefano facchini gmail com>
Date:   Sat Sep 8 13:39:17 2012 +0200

    Add ability to save and load reports in JSON format
    
    https://bugzilla.gnome.org/show_bug.cgi?id=633293

 configure.ac             |    1 +
 src/Makefile.am          |    3 +-
 src/baobab-location.vala |   35 ++++++++++++++++
 src/baobab-scanner.vala  |   99 ++++++++++++++++++++++++++++++++++++++++++++-
 src/baobab-window.vala   |   59 +++++++++++++++++++++++++++-
 5 files changed, 192 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b394aad..bbbd971 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ YELP_HELP_INIT
 PKG_CHECK_MODULES(BAOBAB, [
     gtk+-3.0 >= 3.5.9
     gio-2.0 >= 2.30.0
+    json-glib-1.0
 ])
 
 LT_INIT([disable-static])
diff --git a/src/Makefile.am b/src/Makefile.am
index 9f72c9c..c1c269d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,7 +20,8 @@ baobab_VALAFLAGS = \
 	--pkg gtk+-3.0		\
 	--pkg gio-2.0		\
 	--pkg gio-unix-2.0	\
-	--pkg gd-1.0
+	--pkg gd-1.0		\
+	--pkg json-glib-1.0
 
 noinst_HEADERS = \
 	baobab-chart.h			\
diff --git a/src/baobab-location.vala b/src/baobab-location.vala
index 7eb30f1..261e040 100644
--- a/src/baobab-location.vala
+++ b/src/baobab-location.vala
@@ -129,10 +129,45 @@ namespace Baobab {
             scanner = new Scanner (file, ScanFlags.NONE);
         }
 
+
+        public Location.load_from_file (File report_file) throws Error {
+            var parser = new Json.Parser ();
+            parser.load_from_file (report_file.get_path ());
+
+            var root_object = parser.get_root ().get_object ();
+
+            var name_ = root_object.get_string_member ("name");
+            var unix_time = root_object.get_int_member ("timestamp");
+            var timestamp = new DateTime.from_unix_local (unix_time);
+            var timestamp_label = timestamp.format ("%c");
+
+            name = name_ + " (snapshot " + timestamp_label + ")";
+
+            var scanner_object = root_object.get_object_member ("scanner");
+            scanner = new Scanner.from_json_object (scanner_object);
+        }
+
         public void update () {
             update_volume_info ();
         }
 
+        public void save_to_file (string filename) throws Error {
+            var root_object = new Json.Object ();
+            root_object.set_string_member ("name", name);
+
+            var unix_time = get_real_time () / 1000000;
+            root_object.set_int_member ("timestamp", unix_time);
+
+            var scanner_object = scanner.to_json_object ();
+            root_object.set_object_member ("scanner", scanner_object);
+
+            var root_node = new Json.Node (Json.NodeType.OBJECT);
+            root_node.set_object (root_object);
+
+            var generator = new Json.Generator () { pretty = true, root = root_node };
+            generator.to_file (filename);
+        }
+
         void update_volume_info () {
             mount = volume.get_mount ();
 
diff --git a/src/baobab-scanner.vala b/src/baobab-scanner.vala
index 3622457..c4359a1 100644
--- a/src/baobab-scanner.vala
+++ b/src/baobab-scanner.vala
@@ -375,13 +375,106 @@ namespace Baobab {
             }
         }
 
+        void save_node (Gtk.TreeIter iter, Json.Array json_array) {
+            string name;
+            string parse_name;
+            uint64 alloc_size;
+            double percent;
+            int elements;
+            get (iter,
+                 Scanner.Columns.DISPLAY_NAME, out name,
+                 Scanner.Columns.PARSE_NAME, out parse_name,
+                 Scanner.Columns.PERCENT, out percent,
+                 Scanner.Columns.ALLOC_SIZE, out alloc_size,
+                 Scanner.Columns.ELEMENTS, out elements);
+
+            var obj = new Json.Object ();
+            obj.set_string_member ("n", name);
+            obj.set_string_member ("p", parse_name);
+            obj.set_double_member ("s", (double) alloc_size);
+            obj.set_double_member ("per", percent);
+            obj.set_int_member ("e", elements);
+
+            json_array.add_object_element (obj);
+
+            Gtk.TreeIter child_iter;
+            if (!iter_children (out child_iter, iter))
+                return;
+
+            var children_array = new Json.Array ();
+            obj.set_array_member ("c", children_array);
+
+            do {
+                save_node (child_iter, children_array);
+            } while (iter_next (ref child_iter));
+        }
+
+        public Json.Object to_json_object () {
+            var obj = new Json.Object ();
+
+            obj.set_int_member ("flags", scan_flags);
+
+            Gtk.TreeIter first;
+            get_iter_first (out first);
+            var tree_array = new Json.Array ();
+            save_node (first, tree_array);
+
+            obj.set_array_member ("tree", tree_array);
+
+            return obj;
+        }
+
+        void load_children (Gtk.TreeIter? parent, Json.Array? children_array, int branch_depth) {
+            children_array.foreach_element ((array, index, node) => {
+                var obj = node.get_object ();
+                string name = obj.get_string_member ("n");
+                string parse_name = obj.get_string_member ("p");
+                uint64 alloc_size = (uint64) obj.get_double_member ("s");
+                double percent = obj.get_double_member ("per");
+                int elements = (int) obj.get_int_member ("e");
+
+                Gtk.TreeIter iter;
+                append (out iter, parent);
+                set (iter,
+                     Scanner.Columns.DISPLAY_NAME, name,
+                     Scanner.Columns.PARSE_NAME, parse_name,
+                     Scanner.Columns.PERCENT, percent,
+                     Scanner.Columns.ALLOC_SIZE, alloc_size,
+                     Scanner.Columns.ELEMENTS, elements);
+
+                if (branch_depth > max_depth) {
+                    max_depth = branch_depth;
+                }
+
+                if (obj.has_member ("c")) {
+                    load_children (iter, obj.get_array_member ("c"), branch_depth + 1);
+                }
+            });
+        }
+
+        public Scanner.from_json_object (Json.Object obj) {
+            var tree_array = obj.get_array_member ("tree");
+            var path = tree_array.get_object_element (0).get_string_member ("p");
+            directory = File.new_for_path (path);
+            scan_flags = (ScanFlags) obj.get_int_member ("flags");
+
+            setup_scanner ();
+            load_children (null, tree_array, 0);
+        }
+
         public Scanner (File directory, ScanFlags flags) {
             this.directory = directory;
             this.scan_flags = flags;
+
+            setup_scanner ();
+        }
+
+        void setup_scanner () {
             cancellable = new Cancellable();
             scan_error = null;
+
             set_column_types (new Type[] {
-                typeof (string),  // DIR_NAME
+                typeof (string),  // DISPLAY_NAME
                 typeof (string),  // PARSE_NAME
                 typeof (double),  // PERCENT
                 typeof (uint64),  // SIZE
@@ -394,13 +487,13 @@ namespace Baobab {
 
             excluded_locations = Application.get_excluded_locations ();
 
-            if (ScanFlags.EXCLUDE_MOUNTS in flags) {
+            if (ScanFlags.EXCLUDE_MOUNTS in this.scan_flags) {
                 foreach (unowned UnixMountEntry mount in UnixMountEntry.get (null)) {
                     excluded_locations.add (File.new_for_path (mount.get_mount_path ()));
                 }
             }
 
-            excluded_locations.remove (directory);
+            excluded_locations.remove (this.directory);
 
             results_queue = new AsyncQueue<ResultsArray> ();
         }
diff --git a/src/baobab-window.vala b/src/baobab-window.vala
index 8a4cc0a..b2736da 100644
--- a/src/baobab-window.vala
+++ b/src/baobab-window.vala
@@ -60,6 +60,8 @@ namespace Baobab {
             { "show-allocated", on_show_allocated },
             { "expand-all", on_expand_all },
             { "collapse-all", on_collapse_all },
+            { "save-report", on_save_report },
+            { "load-report", on_load_report },
             { "help", on_help_activate },
             { "about", on_about_activate }
         };
@@ -133,6 +135,8 @@ namespace Baobab {
             var button_box = builder.get_object ("scan-button-box") as Gtk.ButtonBox;
             scan_remote = builder.get_object ("scan-remote-button") as Gtk.Button;
             toolbar.add_widget (button_box, true);
+            var button = toolbar.add_button (null, "Load report", false) as Gtk.Button;
+            button.action_name = "win.load-report";
             toolbar.show_all ();
 
             // Result page toolbar
@@ -142,8 +146,12 @@ namespace Baobab {
             show_home_page_button.action_name = "win.show-home-page";
             stop_button = toolbar.add_button ("process-stop-symbolic", null, true) as Gtk.Button;
             stop_button.action_name = "win.show-home-page";
-            var button = toolbar.add_button ("view-refresh-symbolic", null, false) as Gtk.Button;
+            button = toolbar.add_button ("view-refresh-symbolic", null, true) as Gtk.Button;
             button.action_name = "win.reload";
+            button = toolbar.add_button (null, "Save Report", false) as Gtk.Button;
+            button.action_name = "win.save-report";
+            button = toolbar.add_button (null, "Load report", false) as Gtk.Button;
+            button.action_name = "win.load-report";
             toolbar.show_all ();
 
             location_list.set_adjustment (location_scroll.get_vadjustment ());
@@ -205,6 +213,55 @@ namespace Baobab {
             action.set_state (value);
         }
 
+
+        void on_save_report () {
+            var file_chooser = new Gtk.FileChooserDialog (_("Save report as"), this,
+                                                          Gtk.FileChooserAction.SAVE,
+                                                          Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+                                                          Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT);
+            file_chooser.set_modal (true);
+
+            file_chooser.response.connect ((response) => {
+                if (response == Gtk.ResponseType.ACCEPT) {
+                    try {
+                        active_location.save_to_file (file_chooser.get_filename ());
+                    } catch (Error e) {
+                        message (_("Could not save report."), e.message, Gtk.MessageType.ERROR);
+                    }
+                }
+                file_chooser.destroy ();
+            });
+
+            file_chooser.show ();
+        }
+
+        void on_load_report () {
+            var file_chooser = new Gtk.FileChooserDialog (_("Open report"), this,
+                                                          Gtk.FileChooserAction.OPEN,
+                                                          Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+                                                          Gtk.Stock.OPEN, Gtk.ResponseType.ACCEPT);
+            file_chooser.set_modal (true);
+
+            file_chooser.response.connect ((response) => {
+                if (response == Gtk.ResponseType.ACCEPT) {
+                    clear_message ();
+                    set_ui_state (UIPage.RESULT, true);
+                    try {
+                        var location = new Location.load_from_file (file_chooser.get_file ());
+                        set_active_location (location);
+                        set_model (location.scanner);
+                        set_ui_state (UIPage.RESULT, false);
+                    } catch (Error e) {
+                        message (_("Could not load report."), e.message, Gtk.MessageType.ERROR);
+                        set_ui_state (UIPage.HOME, false);
+                    }
+                }
+                file_chooser.destroy ();
+            });
+
+            file_chooser.show ();
+        }
+
         void on_scan_home_activate () {
             scan_directory (File.new_for_path (GLib.Environment.get_home_dir ()));
         }



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