[gnome-usage/tracker-powered-storage-view-wip: 80/103] storage: Add the new Storage panel



commit 1f92dc8fec5aa09b1d78112d4c1adc3bf2eb8d0a
Author: Felipe Borges <felipeborges gnome org>
Date:   Thu Mar 22 11:07:29 2018 +0100

    storage: Add the new Storage panel
    
    See #29

 data/interface/adwaita.css          |  51 ++++++++--
 data/org.gnome.Usage.gresource.xml  |   1 +
 data/ui/storage-view-row.ui         |  55 +++++++++++
 data/ui/storage-view.ui             |  99 ++++++++++---------
 src/meson.build                     |   6 ++
 src/storage-graph.vala              | 138 ++++++++++++--------------
 src/storage-view.vala               |   4 -
 src/storage/query-builder.vala      |  32 ++++++
 src/storage/storage-view-item.vala  | 103 ++++++++++++++++++++
 src/storage/storage-view-row.vala   |  69 +++++++++++++
 src/storage/storage-view.vala       | 187 ++++++++++++++++++++++++++++++++++++
 src/storage/tracker-controller.vala | 123 ++++++++++++++++++++++++
 src/storage/tracker-worker.vala     |  44 +++++++++
 src/window.vala                     |   2 +-
 14 files changed, 779 insertions(+), 135 deletions(-)
---
diff --git a/data/interface/adwaita.css b/data/interface/adwaita.css
index db63a7c..3fc3578 100644
--- a/data/interface/adwaita.css
+++ b/data/interface/adwaita.css
@@ -82,14 +82,6 @@ ColorRectangle.available-storage, StorageGraph.available-storage {
     color: #d3d7cf;
 }
 
-row.folders, list.folders {
-    color: #457fd3;
-}
-
-box.storage {
-    background-color: #ffffff;
-}
-
 /* Speedometer */
 .speedometer-outter, .speedometer-inner, .speedometer-content-area {
     border-radius: 50%;
@@ -129,3 +121,46 @@ box.storage {
 .tag.tag-user {
     background: alpha(@theme_fg_color, 0.4);
 }
+
+.row_tag {
+    border-radius: 100%;
+}
+
+/* Storage */
+.storage-list:last-child {
+    background: @theme_bg_color;
+}
+
+.row-tag {
+    border-radius: 100%;
+}
+
+.used-tag {
+    background-image:
+        linear-gradient(90deg,
+                       #737373 33%,
+                       #ffe451 33%,
+                       #ffe451 66%,
+                       #ff002c 66%);
+}
+
+.available-tag { background: white; border: 1px solid @borders; }
+.os-tag { background: black; }
+
+.folders { background: #737373; }
+.files { background: #cdcdcd; }
+.downloads { background: #ffe451; }
+.pictures { background: #2493d3; }
+.videos { background: #a77aa5; }
+.documents { background: #7be95a; }
+.music { background: #f9a14a; }
+
+.stack-children .storage-row {
+    border-bottom: 1px solid @borders;    
+}
+
+.stack-children { background: #fafafa; } /* FIXME: Doesn't work with other themes. */
+
+box.storage {
+    background-color: #ffffff;
+}
diff --git a/data/org.gnome.Usage.gresource.xml b/data/org.gnome.Usage.gresource.xml
index de8eece..490df5a 100644
--- a/data/org.gnome.Usage.gresource.xml
+++ b/data/org.gnome.Usage.gresource.xml
@@ -11,6 +11,7 @@
         <file preprocess="xml-stripblanks">ui/speedometer.ui</file>
         <file preprocess="xml-stripblanks">ui/storage-actionbar.ui</file>
         <file preprocess="xml-stripblanks">ui/storage-view.ui</file>
+        <file preprocess="xml-stripblanks">ui/storage-view-row.ui</file>
         <file preprocess="xml-stripblanks">ui/process-row.ui</file>
         <file preprocess="xml-stripblanks">ui/swap-speedometer.ui</file>
     </gresource>
diff --git a/data/ui/storage-view-row.ui b/data/ui/storage-view-row.ui
new file mode 100644
index 0000000..1a57d49
--- /dev/null
+++ b/data/ui/storage-view-row.ui
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.9 -->
+  <template class="UsageStorageViewRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <style>
+      <class name="storage-row"/>
+    </style>
+
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="spacing">10</property>
+        <property name="margin">10</property>
+
+        <child>
+          <object class="GtkBox" id="tag">
+            <property name="visible">True</property>
+            <property name="width-request">15</property>
+            <property name="height-request">15</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <style>
+              <class name="row-tag"/>
+              <class name="circular"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkLabel" id="title">
+            <property name="visible">True</property>
+            <property name="ellipsize">middle</property>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkLabel" id="size_label">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/storage-view.ui b/data/ui/storage-view.ui
index 00b1974..5296a6a 100644
--- a/data/ui/storage-view.ui
+++ b/data/ui/storage-view.ui
@@ -1,82 +1,85 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.9 -->
-  <template class="UsageStorageView" parent="UsageView">
+  <template class="UsageNewStorageView" parent="UsageView">
     <property name="visible">True</property>
     <child>
       <object class="GtkStack" id="stack">
         <property name="visible">True</property>
         <child>
-          <object class="GtkSpinner">
+          <object class="GtkPaned">
             <property name="visible">True</property>
-            <property name="active">True</property>
-          </object>
-          <packing>
-            <property name="name">spinner</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="orientation">horizontal</property>
+            <property name="position">360</property>
             <child>
-              <object class="GtkPaned" id="paned">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="orientation">horizontal</property>
-                <property name="position">300</property>
+                <property name="orientation">vertical</property>
+                <style><class name="view"/></style>
+
+                <child>
+                  <object class="GtkLabel" id="header_label">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                    <property name="margin">10</property>
+                    <property name="label" translatable="yes">Home Partition</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+
                 <child>
-                  <object class="GtkScrolledWindow" id="scrolled_window">
+                  <object class="UsageStorageViewRow" id="used_row">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Used</property>
+                    <property name="tag_size">1</property>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="UsageStorageViewRow" id="available_row">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Available</property>
+                    <property name="tag_size">1</property>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="DzlStackList" id="listbox">
                     <property name="visible">True</property>
                     <property name="vexpand">True</property>
+                    <style>
+                      <class name="storage-list"/>
+                    </style>
                   </object>
                 </child>
               </object>
             </child>
             <child>
-              <object class="GtkRevealer" id="revealer">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="transition-type">slide-up</property>
-                <property name="transition-duration">400</property>
+                <property name="vexpand">True</property>
+                <child>
+                  <object class="UsageStorageGraph" id="graph">
+                    <property name="visible">True</property>
+                    <property name="expand">True</property>
+                  </object>
+                </child>
               </object>
             </child>
           </object>
-          <packing>
-            <property name="name">content</property>
-          </packing>
         </child>
         <child>
-          <object class="GtkBox">
+          <object class="GtkSpinner">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
-            <property name="valign">center</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="icon-name">folder-symbolic</property>
-                <property name="pixel-size">128</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="margin-top">10</property>
-                <property name="label" translatable="yes">No content here</property>
-                <style>
-                  <class name="dim-label"/>
-                </style>
-                <attributes>
-                  <attribute name="weight" value="bold"/>
-                  <attribute name="scale" value="1.66"/>
-                </attributes>
-              </object>
-            </child>
+            <property name="active">True</property>
           </object>
           <packing>
-            <property name="name">empty</property>
+            <property name="name">spinner</property>
           </packing>
         </child>
       </object>
     </child>
-
   </template>
 </interface>
diff --git a/src/meson.build b/src/meson.build
index 7e44f2b..452cb8d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -29,6 +29,12 @@ vala_sources = [
   'quit-process-dialog.vala',
   'settings.vala',
   'speedometer.vala',
+  'storage/query-builder.vala',
+  'storage/storage-view-item.vala',
+  'storage/storage-view.vala',
+  'storage/storage-view-row.vala',
+  'storage/tracker-controller.vala',
+  'storage/tracker-worker.vala',
   'storage-actionbar.vala',
   'storage-analyzer.vala',
   'storage-graph.vala',
diff --git a/src/storage-graph.vala b/src/storage-graph.vala
index 6605640..87234c1 100644
--- a/src/storage-graph.vala
+++ b/src/storage-graph.vala
@@ -24,6 +24,15 @@ namespace Usage
 {
     public class StorageGraph : Gtk.DrawingArea
     {
+        private GLib.ListStore _model;
+        public GLib.ListStore model {
+            set {
+                _model = value;
+                this.draw.connect(draw_storage_graph);
+                this.queue_draw ();
+            }
+            get { return _model; }
+        }
         public const uint MIN_PERCENTAGE_SHOWN_FILES = 2;
 
         class construct
@@ -33,7 +42,6 @@ namespace Usage
 
         public StorageGraph()
         {
-            this.draw.connect(draw_storage_graph);
         }
 
         public enum Circle
@@ -48,63 +56,35 @@ namespace Usage
             double start_angle = 0;
             double final_angle = - Math.PI / 2.0;
             double ratio = 0;
-            var fill_color = Gdk.RGBA();
             var background_color = 
get_toplevel().get_style_context().get_background_color(get_toplevel().get_style_context().get_state());
 
             for(int i = 0; i < model.get_n_items(); i++)
             {
-                StorageItem item = (StorageItem) model.get_item(i);
-                if(item.get_percentage() > 0 && item.get_item_type() != StorageItemType.STORAGE && 
item.get_section() == section)
-                {
+                var item = model.get_item(i) as StorageViewItem;
+                //StorageViewRow item = (StorageViewRow) model.get_item(i);
+                //if(item.percentage > 0)
+                //{
                     var style_context = get_style_context();
-                    switch(item.get_item_type())
-                    {
-                        case StorageItemType.SYSTEM:
-                            style_context.add_class("system");
-                            fill_color = style_context.get_color(style_context.get_state());
-                            style_context.remove_class("system");
-                            break;
-                        case StorageItemType.TRASH:
-                            style_context.add_class("trash");
-                            fill_color = style_context.get_color(style_context.get_state());
-                            style_context.remove_class("trash");
-                            break;
-                        case StorageItemType.USER:
-                            style_context.add_class("user");
-                            fill_color = style_context.get_color(style_context.get_state());
-                            style_context.remove_class("user");
-                            break;
-                        case StorageItemType.AVAILABLE:
-                            style_context.add_class("available-storage");
-                            fill_color = style_context.get_color(style_context.get_state());
-                            style_context.remove_class("available-storage");
-                            break;
-                        case StorageItemType.DOCUMENTS:
-                        case StorageItemType.DOWNLOADS:
-                        case StorageItemType.DESKTOP:
-                        case StorageItemType.MUSIC:
-                        case StorageItemType.PICTURES:
-                        case StorageItemType.VIDEOS:
-                        case StorageItemType.DIRECTORY:
-                        case StorageItemType.FILE:
-                            fill_color = item.get_color();
-                            break;
-                    }
+                    style_context.add_class(item.style_class);
+                    var base_color = style_context.get_background_color(style_context.get_state());
+                    style_context.remove_class(item.style_class);
+
+                    Gdk.RGBA fill_color = generate_color(base_color, i, model.get_n_items ());
                     context.set_line_width (2.0);
                     start_angle = final_angle;
-                    ratio = ratio + ((double) item.get_percentage() / 100);
+                    ratio = ratio + ((double) item.percentage / 100);
                     final_angle = ratio * 2 * Math.PI - Math.PI / 2.0;
                     context.move_to (x, y);
                     Gdk.cairo_set_source_rgba (context, fill_color);
                     context.arc (x, y, radius, start_angle, final_angle);
-                    if(item.get_percentage() == 100)
+                    if(item.percentage == 100)
                         context.fill();
                     else
                         context.fill_preserve();
                     Gdk.cairo_set_source_rgba (context, background_color);
                     context.stroke();
 
-                    if(item.get_percentage() > MIN_PERCENTAGE_SHOWN_FILES)
+                    if(item.percentage > MIN_PERCENTAGE_SHOWN_FILES)
                     {
                         double midle_angle = start_angle + (final_angle - start_angle) / 2;
                         midle_angle += Math.PI / 2;
@@ -166,10 +146,10 @@ namespace Usage
                         }
                         space -= SIDE_MARGIN;
                         x_text += SIDE_MARGIN;
-                        draw_text(context, item.get_name(), x_text, y_text, quadrant, space);
+                        draw_text(context, item.name, x_text, y_text, quadrant, space);
                     }
                 }
-            }
+            //}
         }
 
         private void draw_text(Cairo.Context context, string text, double x, double y, CornerType 
start_corner, double width)
@@ -204,45 +184,55 @@ namespace Usage
             int height = this.get_allocated_height ();
             int width = this.get_allocated_width ();
 
-            var storage_list_box = ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[Views.STORAGE]).get_storage_list_box();
-            var model = storage_list_box.get_model();
-
-            var two_graphs = false;
-            if(storage_list_box.get_root() && StorageAnalyzer.get_default().get_separate_home())
-                two_graphs = true;
-
             double x = 0;
             double y = 0;
             double radius = 0;
-            if(two_graphs)
+
+            radius = int.min (width, height) / 2.0;
+            radius -= radius / 3;
+            x = width / 2.0;
+            y = height / 2.0;
+
+            draw_circle(context, model, x, y, radius, 0, Circle.HOME);
+
+            return true;
+        }
+
+        private Gdk.RGBA generate_color(Gdk.RGBA default_color, uint order, uint all_color, bool reverse = 
false)
+        {
+            if(order >= all_color)
+                order = all_color - 1;
+
+            order += 1;
+
+            double step = 100 / all_color;
+            if(all_color % 2 == 1)
+            {
+                if((all_color / 2) + 1 == order)
+                    return default_color;
+                else
+                    step = 100 / (all_color - 1);
+            }
+
+            if(order > (all_color / 2))
             {
-                double border = 5.5;
-                radius = int.min (width, height) / 3.5;
-                x = width / 1.8;
-                x -= x / border;
-                y = height / 2.1;
-                y -= y / border;
-                draw_circle(context, model, x, y, radius, 0, Circle.HOME);
-
-                border = 1.75;
-                radius = int.min (width, height) / 11.0;
-                x = width / 2.0;
-                x += x / border;
-                y = height / 1.9;
-                y += y / border;
-                draw_circle(context, model, x, y, radius, 1, Circle.ROOT);
+                if(all_color % 2 == 1)
+                    order -= 1;
+                double percentage = step * (order - (all_color/2));
+                if(reverse)
+                    return Utils.color_lighter(default_color, percentage);
+                else
+                    return Utils.color_darker(default_color, percentage);
             }
             else
             {
-                radius = int.min (width, height) / 2.0;
-                radius -= radius / 3;
-                x = width / 2.0;
-                y = height / 2.0;
-
-                draw_circle(context, model, x, y, radius, 0, Circle.BASE);
+                double percentage = step * ((all_color/2) - (order-1));
+                if(reverse)
+                    return Utils.color_darker(default_color, percentage);
+                else
+                    return Utils.color_lighter(default_color, percentage);
             }
-
-            return true;
         }
+
     }
 }
diff --git a/src/storage-view.vala b/src/storage-view.vala
index 3ab1d0b..e26974b 100644
--- a/src/storage-view.vala
+++ b/src/storage-view.vala
@@ -27,16 +27,12 @@ namespace Usage
         private StorageListBox storage_list_box;
         private StorageActionBar action_bar;
 
-        [GtkChild]
         private Gtk.Revealer revealer;
 
-        [GtkChild]
         private Gtk.Stack stack;
 
-        [GtkChild]
         private Gtk.ScrolledWindow scrolled_window;
 
-        [GtkChild]
         private Gtk.Paned paned;
 
         public StorageView ()
diff --git a/src/storage/query-builder.vala b/src/storage/query-builder.vala
new file mode 100644
index 0000000..4ca82cb
--- /dev/null
+++ b/src/storage/query-builder.vala
@@ -0,0 +1,32 @@
+/* query-builder.vala
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+public class Usage.StorageQueryBuilder {
+    public string enumerate_children (string uri, bool recursive = false) {
+        string filter;
+        if (recursive) {
+            filter = @"tracker:uri-is-descendant ('$uri', ?uri)";  
+        } else {
+            filter = @"tracker:uri-is-parent ('$uri', ?uri)";
+        }
+
+        return @"SELECT ?uri rdf:type(?u) { ?u nie:url ?uri . FILTER($filter) }";
+    }
+}
diff --git a/src/storage/storage-view-item.vala b/src/storage/storage-view-item.vala
new file mode 100644
index 0000000..54e0a52
--- /dev/null
+++ b/src/storage/storage-view-item.vala
@@ -0,0 +1,103 @@
+/* storage-view-item.vala
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+public class Usage.StorageViewItem : GLib.Object {
+    public double percentage { set; get; }
+
+    public string uri;
+    public string name;
+    public uint64 size;
+
+    public FileType type;
+    public UserDirectory? dir;
+    public string ontology;
+
+    private string _style_class = null;
+    public string style_class {
+        protected set {
+            _style_class = value;
+        }
+        get {
+            if (_style_class != null)
+                return _style_class;
+
+            setup_tag_style ();
+
+            return _style_class;
+        }
+    }
+
+    public StorageViewItem.from_file (File file) {
+        uri = file.get_uri ();
+
+        try {
+        var info = file.query_info (FileAttribute.STANDARD_SIZE + "," + FileAttribute.STANDARD_NAME + "," + 
FileAttribute.STANDARD_TYPE + "," + FileAttribute.TRASH_ORIG_PATH, FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
+        name = info.get_name ();
+        size = info.get_size ();
+        type = info.get_file_type ();
+        } catch (GLib.Error error) {
+            warning (error.message);
+        }
+    }
+
+    private void setup_tag_style () {
+        if (type == FileType.DIRECTORY) {
+            style_class = "folders";
+        }
+
+        if (dir != null) {
+            switch (dir) {
+                case UserDirectory.PICTURES:
+                    style_class = "pictures";
+                    break;
+                case UserDirectory.VIDEOS:
+                    style_class = "videos";
+                    break;
+                case UserDirectory.DOCUMENTS:
+                    style_class = "documents";
+                    break;
+                case UserDirectory.MUSIC:
+                    style_class = "music";
+                    break;
+            }
+        } else {
+            switch (ontology) {
+                case "nmm#MusicPiece":
+                    style_class = "music";
+                    break;
+                 case "nmm#Photo":
+                    style_class = "pictures";
+                    break;
+                 case "nmm#Video":
+                    style_class = "videos";
+                    break;
+                 case "nfo#PaginatedTextDocument":
+                 case "nfo#PlainTextDocument":
+                 case "nfo#FileDataObject":
+                 case "nfo#EBook":
+                    style_class = "documents";
+                    break;
+            }
+        }
+
+        if (_style_class == null)
+            style_class = "files";
+    }
+}
diff --git a/src/storage/storage-view-row.vala b/src/storage/storage-view-row.vala
new file mode 100644
index 0000000..2e9170b
--- /dev/null
+++ b/src/storage/storage-view-row.vala
@@ -0,0 +1,69 @@
+/* storage-view-row.vala
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+[GtkTemplate (ui = "/org/gnome/Usage/ui/storage-view-row.ui")]
+public class Usage.StorageViewRow : Gtk.ListBoxRow {
+    public string label {
+        set {
+            title.label = value;
+        }
+        get {
+            return title.label;
+        }
+    }
+
+    [GtkChild]
+    private Gtk.Label title;
+
+    [GtkChild]
+    public Gtk.Label size_label;
+
+    [GtkChild]
+    public Gtk.Box tag;
+
+    public enum TagSize {
+        SMALL,
+        BIG,
+    }
+    public TagSize tag_size {
+        set {
+            if (value == TagSize.BIG) {
+                tag.width_request = tag.height_request = 20;
+            }
+        }
+        get {
+            return (tag.width_request == 20 ? TagSize.BIG : TagSize.SMALL);
+        }
+    }
+
+    public StorageViewItem item; 
+
+    public StorageViewRow.from_item (StorageViewItem item) {
+        this.item = item;
+
+        title.label = item.name;    
+        size_label.label = Utils.format_size_values (item.size);
+
+        tag.get_style_context ().add_class (item.style_class);
+
+        if (item.type == FileType.DIRECTORY)
+            tag.width_request = tag.height_request = 20;
+    }
+}
diff --git a/src/storage/storage-view.vala b/src/storage/storage-view.vala
new file mode 100644
index 0000000..ef4d930
--- /dev/null
+++ b/src/storage/storage-view.vala
@@ -0,0 +1,187 @@
+/* storage-view.vala
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+using Tracker;
+using GTop;
+
+[GtkTemplate (ui = "/org/gnome/Usage/ui/storage-view.ui")]
+public class Usage.NewStorageView : Usage.View {
+    private Sparql.Connection connection;
+    private TrackerController controller;
+    private StorageQueryBuilder query_builder;
+
+    [GtkChild]
+    private Gtk.Label header_label;
+
+    [GtkChild]
+    private StorageViewRow used_row;
+
+    [GtkChild]
+    private StorageViewRow available_row;
+
+    [GtkChild]
+    private Dazzle.StackList listbox;
+
+    [GtkChild]
+    private StorageGraph graph;
+
+    private StorageViewRow os_row = new StorageViewRow ();
+
+    private UserDirectory[] xdg_folders = {
+        UserDirectory.DOCUMENTS,
+        UserDirectory.DOWNLOAD,
+        UserDirectory.MUSIC,
+        UserDirectory.PICTURES,
+        UserDirectory.VIDEOS,
+    };
+
+    construct {
+        name = "STORAGE";
+        title = _("Storage");
+
+        try {
+            connection = Sparql.Connection.get ();
+        } catch (GLib.Error error) {
+            critical ("Failed to connect to Tracker: %s", error.message);
+        }
+
+        query_builder = new StorageQueryBuilder ();
+        controller = new TrackerController (connection);
+    }
+
+    public NewStorageView () {
+        listbox.row_activated.connect (on_row_activated);
+
+        setup_header_label ();
+        setup_mount_sizes ();
+        populate_view ();
+    }
+
+    private void on_row_activated (Gtk.ListBoxRow row) {
+        var storage_row = row as StorageViewRow;
+
+        if (storage_row.item.type == FileType.DIRECTORY) {
+            present_dir (storage_row.item.uri);        
+        } else {
+            graph.queue_draw ();
+        }
+    }
+
+    private string get_user_special_dir_path (UserDirectory dir) {
+        return "file://" + Environment.get_user_special_dir (dir);
+    }
+
+    private Gtk.Widget create_file_row (Object obj) {
+        var item = obj as StorageViewItem;
+
+        var row = new StorageViewRow.from_item (item);
+        row.visible = true;
+
+        if (item.type == FileType.DIRECTORY) {
+            controller.get_file_size.begin (item.uri, (obj, res) => {
+                try {
+                    var size = controller.get_file_size.end (res);
+                    row.size_label.label = Utils.format_size_values (size);
+                } catch (GLib.Error error) {
+                    warning (error.message);
+                }
+            });
+        }
+
+        row.show_all ();
+        return row;
+    }
+
+    private async void present_dir (string uri) {
+        if (connection == null)
+            return;
+
+        try {
+            var model = yield controller.enumerate_children (uri);
+
+            var file = File.new_for_uri (uri);
+            var item = new StorageViewItem.from_file (file);
+            var row = new StorageViewRow.from_item (item);
+
+            listbox.push (row, model, create_file_row);
+
+            graph.model = model;
+        } catch (GLib.Error error) {
+            critical ("Failed to query the store: %s", error.message);
+        }
+    }
+
+    private void setup_header_label () {
+        header_label.label = Environment.get_host_name ();
+    }
+
+    private void setup_mount_sizes () {
+        uint64 total_used_size = 0;
+        uint64 total_free_size = 0;
+
+        MountList mount_list;
+        MountEntry[] entries = GTop.get_mountlist (out mount_list, false);
+
+        for (int i = 0; i < mount_list.number; i++) {
+            string dir = (string) entries[i].mountdir;
+
+            FsUsage mount;
+            GTop.get_fsusage (out mount, dir);
+
+            var total = mount.blocks * mount.block_size;
+            var free = mount.bfree * mount.block_size;
+            var used = total - free;
+
+            if (dir == "/") {
+                os_row.label = _("Operating System");
+                os_row.tag_size = StorageViewRow.TagSize.BIG;
+                os_row.get_style_context ().add_class ("stack-children");
+                os_row.tag.get_style_context ().add_class ("os-tag");
+                os_row.size_label.label = Utils.format_size_values (used);
+            }
+
+            total_used_size += used;
+            total_free_size += free;
+        }
+
+        used_row.size_label.label = Utils.format_size_values (total_used_size);
+        used_row.tag.get_style_context ().add_class ("used-tag");
+
+        available_row.size_label.label = Utils.format_size_values (total_free_size);
+        available_row.tag.get_style_context ().add_class ("available-tag");
+    }
+
+    private async void populate_view () {
+        if (connection == null)
+            return;
+
+        var model = new GLib.ListStore (typeof (StorageViewItem));
+        foreach (var dir in xdg_folders) {
+            var file = File.new_for_uri (get_user_special_dir_path (dir));
+            var item = new StorageViewItem.from_file (file);
+            item.dir = dir;
+
+            model.append (item);
+        }
+
+        listbox.push (os_row, model, create_file_row);
+        graph.model = model;
+    }
+}
diff --git a/src/storage/tracker-controller.vala b/src/storage/tracker-controller.vala
new file mode 100644
index 0000000..d57d33c
--- /dev/null
+++ b/src/storage/tracker-controller.vala
@@ -0,0 +1,123 @@
+/* tracker-controller.vala
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+using Tracker;
+
+public class Usage.TrackerController : GLib.Object {
+    private Sparql.Connection connection;
+    private StorageQueryBuilder query_builder;
+
+    construct {
+        query_builder = new StorageQueryBuilder ();
+    }
+
+    public TrackerController (Sparql.Connection connection) {
+        this.connection = connection;
+    }
+
+    public async GLib.ListStore enumerate_children (string uri) throws GLib.Error {
+        var list = new GLib.ListStore (typeof (StorageViewItem));
+
+        var query = query_builder.enumerate_children (uri);
+
+        var worker = yield new TrackerWorker (connection, query);
+        string n_uri = null;
+        string file_type = null;
+
+        var parent = File.new_for_uri (uri);
+        uint64 parent_size = 1;
+        if (parent != null) {
+            parent_size = yield get_file_size (uri);
+        }
+
+        while (yield worker.fetch_next (out n_uri, out file_type)) {
+            try {
+                var file = File.new_for_uri (n_uri);
+                var item = new StorageViewItem.from_file (file);
+                item.ontology = file_type;
+
+                if (item.type == FileType.DIRECTORY) {
+                    item.size = yield get_file_size (n_uri);
+                }
+
+                item.percentage = item.size*100/(double)parent_size;
+
+                list.insert_sorted (item, (a, b) => {
+                    var item_a = a as StorageViewItem;
+                    var item_b = b as StorageViewItem;
+
+                    if (item_a.type == FileType.DIRECTORY) {
+                        return -1;
+                    }
+
+                    if (item_b.type == FileType.DIRECTORY) {
+                        return 1;
+                    }
+
+                    if (item_a.size > item_b.size) {
+                        return -1;
+                    }
+
+                    if (item_b.size > item_a.size) {
+                        return 1;
+                    }
+
+                    return 0;
+                });
+            } catch (GLib.Error error) {
+                warning (error.message);
+            }
+        }
+
+        return list;
+    }
+
+    private uint64 get_g_file_size (string uri) {
+        try {
+            var file = File.new_for_uri (uri);
+            var info = file.query_info (FileAttribute.STANDARD_SIZE, FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
+
+            return info.get_size ();
+        } catch (GLib.Error error) {
+            warning (error.message);
+        }
+
+        return 0;
+    }
+
+    public async uint64 get_file_size (string uri) throws GLib.Error {
+        uint64 total = 0;
+
+        var query = query_builder.enumerate_children (uri, true);
+
+        var worker = yield new TrackerWorker (connection, query);
+
+        string n_uri = null;
+        while (yield worker.fetch_next (out n_uri, null)) {
+            try {
+                total += get_g_file_size (n_uri);
+            } catch (GLib.Error error) {
+                warning (error.message);
+            }
+        }
+
+        return total;
+    }
+}
diff --git a/src/storage/tracker-worker.vala b/src/storage/tracker-worker.vala
new file mode 100644
index 0000000..545d914
--- /dev/null
+++ b/src/storage/tracker-worker.vala
@@ -0,0 +1,44 @@
+/* tracker-worker.vala
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Felipe Borges <felipeborges gnome org>
+ */
+
+using Tracker;
+
+public class Usage.TrackerWorker {
+    private Sparql.Cursor cursor;
+
+    public async TrackerWorker (Sparql.Connection connection, string query) throws GLib.Error {
+        cursor = yield connection.query_async (query);
+    }
+
+    public async bool fetch_next (out string uri, out string file_type) throws GLib.Error {
+        uri = file_type = null;
+
+        if (!(yield cursor.next_async ()))
+            return false;
+
+        uri = cursor.get_string (0);
+        var type = cursor.get_string (1);
+        file_type = type.substring (type.last_index_of_char ('/') + 1, -1);
+        if (uri == null)
+            return yield fetch_next (out uri, out file_type);
+
+        return true;
+    }
+}
diff --git a/src/window.vala b/src/window.vala
index a4109fc..c4278b6 100644
--- a/src/window.vala
+++ b/src/window.vala
@@ -53,7 +53,7 @@ namespace Usage
             views = new View[]
             {
                 new PerformanceView(),
-                new StorageView(),
+                new NewStorageView(),
             };
 
             foreach(var view in views)



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