[gnome-usage] Added Storage Analyzer. Added ListBox in storage. Enhancements of HeaderBar.



commit 76009d8059de9b7ba7c0d64d8dd1251fb9e16e23
Author: Petr Štětka <pstetka redhat com>
Date:   Wed Feb 22 15:00:45 2017 +0100

    Added Storage Analyzer.
    Added ListBox in storage.
    Enhancements of HeaderBar.

 README.md                       |    2 +-
 data/interface/adwaita-dark.css |   38 +---
 data/interface/adwaita.css      |   36 +---
 gnome-usage.avprj               |   10 +
 po/POTFILES.in                  |    7 +-
 src/application.vala            |    7 +
 src/color-rectangle.vala        |   17 ++-
 src/graph-block-row.vala        |    2 +-
 src/graph-stack-switcher.vala   |    8 +-
 src/header-bar.vala             |   97 ++++++++-
 src/storage-analyzer.vala       |  470 +++++++++++++++++++++++++++++++++++++++
 src/storage-item.vala           |  128 +++++++++++
 src/storage-list-box.vala       |  140 ++++++++++++
 src/storage-row.vala            |  129 +++++++++++
 src/storage-view.vala           |   38 +++
 src/storage-worker.vala         |   87 +++++++
 src/utils.vala                  |   30 +++
 src/vapis/glibtop.vapi          |   36 +++-
 src/window.vala                 |   38 +++-
 19 files changed, 1243 insertions(+), 77 deletions(-)
---
diff --git a/README.md b/README.md
index 3e0beb9..bb15993 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ New GNOME Usage!
 - [x] Support other architectures than x86_64 (netinfo precompiled library) 
 - [ ] Fix bug with refreshing ProcessListBox 50% (focus, and click when refresh)
 - [ ] Search in processes 
-- [ ] Storage view - 1%
+- [ ] Storage view - 75%
 - [ ] Power view (Design?)
 - [ ] Disk usage (What library we can use?)
 - [ ] Data view - 0%
diff --git a/data/interface/adwaita-dark.css b/data/interface/adwaita-dark.css
index 01ebe34..b197f61 100644
--- a/data/interface/adwaita-dark.css
+++ b/data/interface/adwaita-dark.css
@@ -112,45 +112,17 @@ PieChart.available, ColorRectangle.available {
 }
 
 ColorRectangle.system {
-    color: #2e3436;
+    color: #3c4447;
 }
 
 ColorRectangle.applications {
     color: #4e9a06;
 }
 
-ColorRectangle.documents {
-    color: #99b9dc;
-}
-
-ColorRectangle.downloads {
-    color: #9bb8db;
-}
-
-ColorRectangle.music {
-    color: #457fd3;
-}
-
-ColorRectangle.pictures {
-    color: #3466a5;
-}
-
-ColorRectangle.videos {
-    color: #204a87;
-}
-
-ColorRectangle.others-storage {
-    color: #16325c;
-}
-
 ColorRectangle.trash {
     color: #ef2929;
 }
 
-ColorRectangle.cache {
-    color: #fce94f;
-}
-
 ColorRectangle.user {
     color: #ad7fa8;
 }
@@ -158,3 +130,11 @@ ColorRectangle.user {
 ColorRectangle.available-storage {
     color: #d3d7cf;
 }
+
+row.folders {
+    color: #457fd3;
+}
+
+list.folders {
+    color: #457fd3;
+}
\ No newline at end of file
diff --git a/data/interface/adwaita.css b/data/interface/adwaita.css
index 35527cd..9a22685 100644
--- a/data/interface/adwaita.css
+++ b/data/interface/adwaita.css
@@ -125,38 +125,10 @@ ColorRectangle.applications {
     color: #4e9a06;
 }
 
-ColorRectangle.documents {
-    color: #99b9dc;
-}
-
-ColorRectangle.downloads {
-    color: #9bb8db;
-}
-
-ColorRectangle.music {
-    color: #457fd3;
-}
-
-ColorRectangle.pictures {
-    color: #3466a5;
-}
-
-ColorRectangle.videos {
-    color: #204a87;
-}
-
-ColorRectangle.others_storage {
-    color: #16325c;
-}
-
 ColorRectangle.trash {
     color: #ef2929;
 }
 
-ColorRectangle.cache {
-    color: #fce94f;
-}
-
 ColorRectangle.user {
     color: #ad7fa8;
 }
@@ -164,3 +136,11 @@ ColorRectangle.user {
 ColorRectangle.available_storage {
     color: #d3d7cf;
 }
+
+row.folders {
+    color: #457fd3;
+}
+
+list.folders {
+    color: #457fd3;
+}
diff --git a/gnome-usage.avprj b/gnome-usage.avprj
index 31686fb..cde0b5d 100644
--- a/gnome-usage.avprj
+++ b/gnome-usage.avprj
@@ -58,8 +58,13 @@ c_library: rg egg gmodule-2.0 m rt gtop-2.0 pthread dl netinfo
 *vala_source: process-row.vala
 *vala_source: process.vala
 *vala_source: settings.vala
+*vala_source: storage-analyzer.vala
 *vala_source: storage-graph.vala
+*vala_source: storage-item.vala
+*vala_source: storage-list-box.vala
+*vala_source: storage-row.vala
 *vala_source: storage-view.vala
+*vala_source: storage-worker.vala
 *vala_source: sub-process-list-box.vala
 *vala_source: sub-process-sub-row.vala
 *vala_source: system-monitor.vala
@@ -108,8 +113,13 @@ h_folder: /usr/include/libgtop-2.0
 *translate: vala src/process-row.vala
 *translate: vala src/process.vala
 *translate: vala src/settings.vala
+*translate: vala src/storage-analyzer.vala
 *translate: vala src/storage-graph.vala
+*translate: vala src/storage-item.vala
+*translate: vala src/storage-list-box.vala
+*translate: vala src/storage-row.vala
 *translate: vala src/storage-view.vala
+*translate: vala src/storage-worker.vala
 *translate: vala src/sub-process-list-box.vala
 *translate: vala src/sub-process-sub-row.vala
 *translate: vala src/system-monitor.vala
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5083f41..bdb26a0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,17 +2,18 @@
 # Please keep this file sorted alphabetically.
 data/org.gnome.Usage.desktop
 data/org.gnome.Usage.gschema.xml
-external/egg/egg-date-time.c
-external/egg/egg-file-chooser-entry.c
-external/egg/egg-search-bar.c
 src/application.vala
 src/cpu-sub-view.vala
 src/data-view.vala
 src/graph-block.vala
+src/graph-stack-switcher.vala
 src/memory-sub-view.vala
 src/network-sub-view.vala
 src/performance-view.vala
 src/power-view.vala
 src/process-dialog.vala
+src/storage-analyzer.vala
+src/storage-item.vala
 src/storage-view.vala
+src/utils.vala
 src/window.vala
diff --git a/src/application.vala b/src/application.vala
index 5289b4c..fdc120f 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -7,6 +7,7 @@ namespace Usage
         public Settings settings;
         private Window window;
         private SystemMonitor monitor;
+        private StorageAnalyzer storage_analyzer;
 
         private const GLib.ActionEntry app_entries[] =
         {
@@ -19,6 +20,7 @@ namespace Usage
             application_id = "org.gnome.Usage";
             settings = new Settings();
             monitor = new SystemMonitor();
+            storage_analyzer = new StorageAnalyzer();
         }
 
         public SystemMonitor get_system_monitor()
@@ -26,6 +28,11 @@ namespace Usage
             return monitor;
         }
 
+        public StorageAnalyzer get_storage_analyzer()
+        {
+            return storage_analyzer;
+        }
+
         public Window? get_window()
         {
             return window;
diff --git a/src/color-rectangle.vala b/src/color-rectangle.vala
index 8a90578..a5a4542 100644
--- a/src/color-rectangle.vala
+++ b/src/color-rectangle.vala
@@ -10,11 +10,26 @@ namespace Usage {
             set_css_name("ColorRectangle");
         }
 
-        public ColorRectangle(string css_class)
+        public Gdk.RGBA get_color()
+        {
+            return color;
+        }
+
+        public ColorRectangle.new_from_rgba(Gdk.RGBA color)
+        {
+            this.color = color;
+            init();
+        }
+
+        public ColorRectangle.new_from_css(string css_class)
         {
             get_style_context().add_class(css_class);
             color = get_style_context().get_color(get_style_context().get_state());
+            init();
+        }
 
+        private void init()
+        {
             this.height_request = 17;
             this.width_request = 17;
             this.valign = Gtk.Align.CENTER;
diff --git a/src/graph-block-row.vala b/src/graph-block-row.vala
index 4d2f7f3..fde5bf1 100644
--- a/src/graph-block-row.vala
+++ b/src/graph-block-row.vala
@@ -10,7 +10,7 @@ namespace Usage {
         public GraphBlockRow(GraphBlockType type, string label_text, string css_class)
         {
             Object(orientation: Gtk.Orientation.HORIZONTAL);
-            var color_rectangle = new ColorRectangle(css_class);
+            var color_rectangle = new ColorRectangle.new_from_css(css_class);
             var label = new Gtk.Label(label_text);
             label.margin = 5;
             label.ellipsize = Pango.EllipsizeMode.END;
diff --git a/src/graph-stack-switcher.vala b/src/graph-stack-switcher.vala
index 89e1ff7..f35d3e4 100644
--- a/src/graph-stack-switcher.vala
+++ b/src/graph-stack-switcher.vala
@@ -22,10 +22,10 @@ namespace Usage
             this.sub_views = sub_views;
 
             buttons = {
-                new GraphSwitcherButton.processor("Processor"),
-                new GraphSwitcherButton.memory("Memory"),
-                new GraphSwitcherButton.disk("Disk I/O"),
-                new GraphSwitcherButton.network("Network"),
+                new GraphSwitcherButton.processor(_("Processor")),
+                new GraphSwitcherButton.memory(_("Memory")),
+                new GraphSwitcherButton.disk(_("Disk I/O")),
+                new GraphSwitcherButton.network(_("Network")),
             };
 
            foreach(Gtk.ToggleButton button in buttons)
diff --git a/src/header-bar.vala b/src/header-bar.vala
index 5274257..f68a40a 100644
--- a/src/header-bar.vala
+++ b/src/header-bar.vala
@@ -1,20 +1,109 @@
 namespace Usage
 {
+    public enum HeaderBarMode
+    {
+        PERFORMANCE,
+        DATA,
+        STORAGE,
+        POWER
+    }
+
        public class HeaderBar : Gtk.HeaderBar
        {
            private Gtk.StackSwitcher stack_switcher;
-           private Gtk.MenuButton menu_button;
+           private Gtk.Button? storage_back_button;
+           private bool show_storage_back_btn = false;
+           private Gtk.Button? storage_rescan_button;
+           private HeaderBarMode mode;
 
            public HeaderBar(Gtk.Stack stack)
            {
+               mode = HeaderBarMode.PERFORMANCE;
                show_close_button = true;
                stack_switcher = new Gtk.StackSwitcher();
             stack_switcher.halign = Gtk.Align.CENTER;
             stack_switcher.set_stack(stack);
-            set_custom_title(stack_switcher);
-            menu_button = new Gtk.MenuButton();
-            menu_button.add(new Gtk.Image.from_icon_name("open-menu-symbolic", Gtk.IconSize.BUTTON));
+
+            set_mode(HeaderBarMode.PERFORMANCE);
             show_all();
            }
+
+           public void set_mode(HeaderBarMode mode)
+           {
+            switch(this.mode)
+            {
+                case HeaderBarMode.PERFORMANCE:
+                    break;
+                case HeaderBarMode.DATA:
+                    break;
+                case HeaderBarMode.STORAGE:
+                    remove(storage_back_button);
+                    remove(storage_rescan_button);
+                    storage_rescan_button = null;
+                    storage_back_button = null;
+                    break;
+                case HeaderBarMode.POWER:
+                    break;
+            }
+
+            switch(mode)
+            {
+                case HeaderBarMode.PERFORMANCE:
+                    show_stack_switcher();
+                    break;
+                case HeaderBarMode.DATA:
+                    show_stack_switcher();
+                    break;
+                case HeaderBarMode.STORAGE:
+                    show_stack_switcher();
+                    storage_back_button = new Gtk.Button.from_icon_name("go-previous-symbolic");
+                    storage_back_button.clicked.connect(() => {
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().on_back_button_clicked();
+                    });
+                    show_storage_back_button(show_storage_back_btn);
+                    storage_rescan_button = new Gtk.Button.from_icon_name("view-refresh-symbolic");
+                    storage_rescan_button.clicked.connect(() => {
+                        (GLib.Application.get_default() as 
Application).get_storage_analyzer().create_cache.begin(true);
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().reload();
+                    });
+                    storage_rescan_button.show();
+                    pack_start(storage_back_button);
+                    pack_end(storage_rescan_button);
+                    break;
+                case HeaderBarMode.POWER:
+                    show_stack_switcher();
+                    break;
+            }
+            this.mode = mode;
+           }
+
+           public void show_title_text(string title)
+           {
+               set_custom_title(null);
+            set_title(title);
+           }
+
+           public void show_stack_switcher()
+        {
+            set_custom_title(stack_switcher);
+        }
+
+        public void show_storage_back_button(bool show)
+        {
+            if(storage_back_button != null)
+            {
+                if(show)
+                {
+                    storage_back_button.show();
+                    show_storage_back_btn = true;
+                }
+                else
+                {
+                    storage_back_button.hide();
+                    show_storage_back_btn = false;
+                }
+            }
+
+        }
        }
 }
\ No newline at end of file
diff --git a/src/storage-analyzer.vala b/src/storage-analyzer.vala
new file mode 100644
index 0000000..ff31fb8
--- /dev/null
+++ b/src/storage-analyzer.vala
@@ -0,0 +1,470 @@
+using GTop;
+
+namespace Usage
+{
+    public class StorageAnalyzer : Object
+    {
+        public signal void cache_complete();
+        public bool cache { private set; get; }
+        public bool separate_home = false;
+
+        private Cancellable cancellable;
+        private HashTable<string, Directory?> directory_table;
+        private HashTable<string, Storage?> storages;
+        private AsyncQueue<StorageResult> results_queue;
+        private string[] exclude_from_home;
+        private static bool path_null;
+
+        private struct Directory
+        {
+            public uint64 size;
+            public float percentage;
+        }
+
+        private struct Storage
+        {
+            public uint64 used;
+            public uint64 free;
+            public uint64 total;
+            public string name;
+            public string mount_path;
+        }
+
+        private Storage? get_storage(string mount_path)
+        {
+            return storages[mount_path];
+        }
+
+        private Directory? get_directory(string path)
+        {
+            return directory_table[path];
+        }
+
+        public void stop_scanning()
+        {
+            cancellable.cancel();
+        }
+
+        public async List<StorageItem> prepare_items(string? path, Gdk.RGBA color)
+        {
+            if(path == null)
+            {
+                path_null = true;
+                List<StorageItem> items = new List<StorageItem>();
+
+                if(separate_home)
+                {
+                    Storage? home = get_storage("/home");
+                    items.insert_sorted(new StorageItem.storage(_("Storage 1"), home.mount_path, home.total, 
0), (CompareFunc) sort);
+                    add_home_items(ref items, 0);
+                    items.insert_sorted(new StorageItem.available(home.free, ((float) home.free / 
home.total) * 100, 0), (CompareFunc) sort);
+                    Storage? root = get_storage("/");
+                    items.insert_sorted(new StorageItem.storage(_("Storage 2"), root.mount_path, root.total, 
1), (CompareFunc) sort);
+                    add_root_items(ref items, root, 1);
+                    items.insert_sorted(new StorageItem.available(root.free, ((float) root.free / 
root.total) * 100, 1), (CompareFunc) sort);
+                }
+                else
+                {
+                    Storage? root = get_storage("/");
+                    items.insert_sorted(new StorageItem.storage(_("Storage 1"), root.mount_path, root.total, 
0), (CompareFunc) sort);
+                    add_root_items(ref items, root, 0);
+                    add_home_items(ref items, 0);
+                    items.insert_sorted(new StorageItem.available(root.free, ((float) root.free / 
root.total) * 100), (CompareFunc) sort);
+                }
+
+                set_colors(ref items, color);
+                return items;
+            } else
+            {
+                path_null = false;
+                return get_items_for_path(path, color, exclude_from_home);
+            }
+        }
+
+        private static int sort(StorageItem a, StorageItem b)
+        {
+            if(a.get_section() > b.get_section())
+                return 1;
+            else if (a.get_section() < b.get_section())
+                return -1;
+            else
+            {
+                switch(a.get_prefered_position())
+                {
+                    case StorageItemPosition.FIRST:
+                        return -1;
+                    case StorageItemPosition.SECOND:
+                        if(b.get_prefered_position() != StorageItemPosition.FIRST)
+                            return -1;
+                        else
+                            return 1;
+                    case StorageItemPosition.ANYWHERE:
+                        switch(b.get_prefered_position())
+                        {
+                            case StorageItemPosition.FIRST:
+                                return 1;
+                            case StorageItemPosition.SECOND:
+                                return 1;
+                            case StorageItemPosition.ANYWHERE:
+                                if(path_null == true)
+                                    return sort_alphabetically(a.get_name(), b.get_name());
+                                else
+                                    return (int) ((uint64) (a.get_size() < b.get_size()) - (uint64) 
(a.get_size() > b.get_size()));
+                            case StorageItemPosition.PENULTIMATE:
+                                return -1;
+                            case StorageItemPosition.LAST:
+                                return -1;
+                        }
+                        break;
+                    case StorageItemPosition.PENULTIMATE:
+                        if(b.get_prefered_position() != StorageItemPosition.LAST)
+                            return 1;
+                        else
+                            return -1;
+                    case StorageItemPosition.LAST:
+                        return 1;
+                }
+            }
+
+            return 0;
+        }
+
+        private static int sort_alphabetically(string first, string second)
+        {
+            int length = first.length < second.length ? first.length : second.length;
+            for(int i = 0; i < length; i++)
+            {
+                if(first[i] < second[i])
+                    return -1;
+                else if(first[i] > second[i])
+                    return 1;
+            }
+            return first.length < second.length ? -1 : 1;
+        }
+
+        private void set_colors(ref List<StorageItem> items, Gdk.RGBA default_color)
+        {
+            for(int i = 0; i < items.length(); i++)
+            {
+                unowned StorageItem item = items.nth_data(i);
+                switch(item.get_item_type())
+                {
+                    case StorageItemType.DOCUMENTS:
+                    case StorageItemType.DOWNLOADS:
+                    case StorageItemType.DESKTOP:
+                    case StorageItemType.MUSIC:
+                    case StorageItemType.PICTURES:
+                    case StorageItemType.VIDEOS:
+                        item.set_color(generate_color(default_color, i-2, 6, false)); //6 because DOCUMENTS, 
DOWNLOADS, DESKTOP, MUSIC, PICTURES, VIDEOS, 2 because header and Home
+                        break;
+                    case StorageItemType.DIRECTORY:
+                    case StorageItemType.FILE:
+                        item.set_color(generate_color(default_color, i, items.length(), true));
+                        break;
+                }
+            }
+        }
+
+        private Gdk.RGBA generate_color(Gdk.RGBA default_color, int order, uint all_color, bool reverse = 
false)
+        {
+            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))
+            {
+                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
+            {
+                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);
+            }
+        }
+
+        public async void create_cache(bool force_override = false)
+        {
+            if(force_override == true)
+               cache = false;
+
+            if(cache == false)
+            {
+                cache = true;
+                analyze_storages();
+                stop_scanning();
+                cancellable.reset();
+                directory_table.remove_all();
+                SourceFunc callback = create_cache.callback;
+
+                ThreadFunc<void*> run = () => {
+                    scan_cache();
+                    Idle.add((owned) callback);
+                    return null;
+                };
+
+                var thread = new Thread<void*>("storage_analyzer", run);
+                yield;
+                thread.join();
+                cache_complete();
+            }
+        }
+
+        private void scan_cache()
+        {
+            uint64 total_size;
+            if(separate_home)
+            {
+                Storage? home = get_storage("/home");
+                total_size = home.total;
+            }
+            else
+            {
+                Storage? root = get_storage("/");
+                total_size = root.total;
+            }
+
+            var desktop   = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.DESKTOP)), total_size, ref 
cancellable, ref results_queue);
+            var documents = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.DOCUMENTS)), total_size, ref 
cancellable, ref results_queue);
+            var downloads = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.DOWNLOAD)), total_size, ref 
cancellable, ref results_queue);
+            var music     = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.MUSIC)), total_size, ref 
cancellable, ref results_queue);
+            var pictures  = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.PICTURES)), total_size, ref 
cancellable, ref results_queue);
+            var videos    = new 
StorageWorker(File.new_for_path(Environment.get_user_special_dir(UserDirectory.VIDEOS)), total_size, ref 
cancellable, ref results_queue);
+            var trash     = new StorageWorker(File.new_for_path(Environment.get_home_dir() + 
"/.local/share/Trash/files"), total_size, ref cancellable, ref results_queue, _("Trash"));
+            var home      = new StorageWorker(File.new_for_path(Environment.get_home_dir()), total_size, ref 
cancellable, ref results_queue, Environment.get_real_name(), exclude_from_home);
+
+            try {
+                ThreadPool<StorageWorker> threads = new ThreadPool<StorageWorker>.with_owned_data((worker) 
=> {
+                    worker.run();
+                }, (int) get_num_processors(), false);
+
+                threads.add(home);
+                threads.add(desktop);
+                threads.add(documents);
+                threads.add(downloads);
+                threads.add(music);
+                threads.add(pictures);
+                threads.add(videos);
+                threads.add(trash);
+            }
+            catch (ThreadError e)
+            {
+                stderr.printf("%s", e.message);
+            }
+
+            StorageResult result;
+            while ((result = results_queue.try_pop()) != null)
+            {
+                var directory = Directory();
+                directory.size = result.size;
+                directory.percentage = result.percentage;
+                directory_table.insert (result.path, directory);
+            }
+        }
+
+        private void add_root_items(ref List<StorageItem> items, Storage storage, int section)
+        {
+            items.insert_sorted(new StorageItem.system(_("Operation System"), storage.used, ((float) 
storage.free / storage.total) * 100, section), (CompareFunc) sort);
+        }
+
+        private void add_home_items(ref List<StorageItem> items, int section)
+        {
+            Directory? user = get_directory(Environment.get_home_dir());
+            if(user != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.USER,
+                    _("Home"),
+                    Environment.get_home_dir(),
+                    user.size,
+                    user.percentage,
+                    section,
+                    StorageItemPosition.SECOND), (CompareFunc) sort);
+            }
+            Directory? desktop = get_directory(Environment.get_user_special_dir(UserDirectory.DESKTOP));
+            if(desktop != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.DESKTOP,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.DESKTOP)),
+                    Environment.get_user_special_dir(UserDirectory.DESKTOP),
+                    desktop.size,
+                    desktop.percentage,
+                    section), (CompareFunc) sort);
+            }
+            Directory? documents = get_directory(Environment.get_user_special_dir(UserDirectory.DOCUMENTS));
+            if(documents != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.DOCUMENTS,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.DOCUMENTS)),
+                    Environment.get_user_special_dir(UserDirectory.DOCUMENTS),
+                    documents.size,
+                    documents.percentage,
+                    section), (CompareFunc) sort);
+            }
+            Directory? downloads = get_directory(Environment.get_user_special_dir(UserDirectory.DOWNLOAD));
+            if(downloads != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.DOWNLOADS,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.DOWNLOAD)),
+                    Environment.get_user_special_dir(UserDirectory.DOWNLOAD),
+                    downloads.size,
+                    downloads.percentage,
+                    section), (CompareFunc) sort);
+            }
+            Directory? music = get_directory(Environment.get_user_special_dir(UserDirectory.MUSIC));
+            if(music != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.MUSIC,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.MUSIC)),
+                    Environment.get_user_special_dir(UserDirectory.MUSIC),
+                    music.size,
+                    music.percentage,
+                    section), (CompareFunc) sort);
+            }
+            Directory? pictures = get_directory(Environment.get_user_special_dir(UserDirectory.PICTURES));
+            if(pictures != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.PICTURES,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.PICTURES)),
+                    Environment.get_user_special_dir(UserDirectory.PICTURES),
+                    pictures.size,
+                    pictures.percentage,
+                    section), (CompareFunc) sort);
+            }
+            Directory? videos = get_directory(Environment.get_user_special_dir(UserDirectory.VIDEOS));
+            if(videos != null)
+            {
+                items.insert_sorted(new StorageItem.item(StorageItemType.VIDEOS,
+                    Path.get_basename(Environment.get_user_special_dir(UserDirectory.VIDEOS)),
+                    Environment.get_user_special_dir(UserDirectory.VIDEOS),
+                    videos.size,
+                    videos.percentage,
+                    section), (CompareFunc) sort);
+            }
+            var trash_path = Environment.get_home_dir() + "/.local/share/Trash/files";
+            Directory? trash = get_directory(trash_path);
+            if(trash != null)
+            {
+                items.insert_sorted(new StorageItem.trash(
+                    trash_path,
+                    trash.size,
+                    trash.percentage,
+                    section), (CompareFunc) sort);
+            }
+        }
+
+        private List<StorageItem> get_items_for_path(string path, Gdk.RGBA color, string[]? exclude_paths = 
null)
+        {
+            List<StorageItem> items = new List<StorageItem>();
+
+            File file = File.new_for_path(path);
+            FileEnumerator enumerator;
+
+            try {
+                enumerator = file.enumerate_children(FileAttribute.STANDARD_SIZE, 
FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+                FileInfo info;
+
+                while((info = enumerator.next_file(null)) != null && cancellable.is_cancelled() == false)
+                {
+                    if(info.get_file_type() == FileType.DIRECTORY)
+                    {
+                        if(exclude_paths == null || !(file.resolve_relative_path(info.get_name()).get_path() 
in exclude_paths))
+                        {
+                            string folder_path = file.get_child(info.get_name()).get_parse_name();
+                            Directory? folder = get_directory(folder_path);
+                            uint64 folder_size = 0;
+                            double folder_percentage = 0;
+
+                            if(folder != null)
+                            {
+                                folder_size = folder.size;
+                                folder_percentage = folder.percentage;
+                            }
+
+                            items.insert_sorted(new StorageItem.directory(info.get_name(), folder_path, 
folder_size,
+                                folder_percentage), (CompareFunc) sort);
+                        }
+                    }
+                    else if(info.get_file_type() == FileType.REGULAR)
+                    {
+                        Directory? parent = get_directory(path);
+                        float percentage = ((float) info.get_size() / parent.size) * 100;
+
+                        items.insert_sorted(new StorageItem.file(info.get_name(),
+                            file.get_child(info.get_name()).get_parse_name(), info.get_size(), percentage),
+                            (CompareFunc) sort);
+                    }
+                }
+            }
+            catch (Error e) {
+                stderr.printf ("Error: %s\n", e.message);
+            }
+
+            set_colors(ref items, color);
+
+            return items;
+        }
+
+        private void analyze_storages()
+        {
+            separate_home = false;
+            storages.remove_all();
+
+            MountList mount_list;
+            MountEntry[] entries = GTop.get_mountlist(out mount_list, false);
+
+            for (int i = 0; i < mount_list.number; i++)
+            {
+                string mountdir = (string) entries[i].mountdir;
+                if(mountdir == "/" || mountdir == "/home")
+                {
+                    if(mountdir == "/home")
+                        separate_home = true;
+
+                    FsUsage root;
+                    GTop.get_fsusage(out root, mountdir);
+
+                    Storage storage = Storage();
+                    storage.free = root.bfree * root.block_size;
+                    storage.total = root.blocks * root.block_size;
+                    storage.used = storage.total - storage.free;
+                    storage.name = (string) entries[i].devname;
+                    storage.mount_path = mountdir;
+                    storages.insert (mountdir, storage);
+                }
+            }
+        }
+
+        public StorageAnalyzer()
+        {
+            cache = false;
+            cancellable = new Cancellable();
+            directory_table = new HashTable<string, Directory?>(str_hash, str_equal);
+            storages = new HashTable<string, Storage?>(str_hash, str_equal);
+            results_queue = new AsyncQueue<StorageResult>();
+            exclude_from_home = {
+                Environment.get_user_special_dir(UserDirectory.DESKTOP),
+                Environment.get_user_special_dir(UserDirectory.DOCUMENTS),
+                Environment.get_user_special_dir(UserDirectory.DOWNLOAD),
+                Environment.get_user_special_dir(UserDirectory.MUSIC),
+                Environment.get_user_special_dir(UserDirectory.PICTURES),
+                Environment.get_user_special_dir(UserDirectory.VIDEOS),
+                Environment.get_home_dir() + "/.local/share/Trash/files"
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/storage-item.vala b/src/storage-item.vala
new file mode 100644
index 0000000..76e6a71
--- /dev/null
+++ b/src/storage-item.vala
@@ -0,0 +1,128 @@
+using Gee;
+
+namespace Usage
+{
+    public enum StorageItemPosition
+    {
+        FIRST,
+        SECOND,
+        ANYWHERE,
+        PENULTIMATE,
+        LAST
+    }
+
+    public enum StorageItemType
+    {
+        STORAGE,
+        SYSTEM,
+        DOCUMENTS,
+        DOWNLOADS,
+        DESKTOP,
+        MUSIC,
+        PICTURES,
+        VIDEOS,
+        TRASH,
+        USER,
+        AVAILABLE,
+        FILE,
+        DIRECTORY
+    }
+
+    public class StorageItem : Object
+    {
+        private string name;
+        private string path;
+        private StorageItemType type;
+        private uint64 size;
+        private double percentage;
+        private StorageItemPosition prefered_position = StorageItemPosition.ANYWHERE;
+        private int section;
+        private Gdk.RGBA color;
+
+        public StorageItem.item(StorageItemType type, string name, string path, uint64 size, double 
percentage, int section = 0, StorageItemPosition prefered_position = StorageItemPosition.ANYWHERE)
+        {
+            this.type = type;
+            this.name = name;
+            this.path = path;
+            this.size = size;
+            this.percentage = percentage;
+            this.section = section;
+            this.prefered_position = prefered_position;
+        }
+
+        public StorageItem.directory(string name, string path, uint64 size, double percentage)
+        {
+            StorageItem.item(StorageItemType.DIRECTORY, name, path, size, percentage);
+        }
+
+        public StorageItem.file(string name, string path, uint64 size, double percentage)
+        {
+            StorageItem.item(StorageItemType.FILE, name, path, size, percentage);
+        }
+
+        public StorageItem.trash(string path, uint64 size, double percentage, int section = 0)
+        {
+            StorageItem.item(StorageItemType.TRASH, _("Trash"), path, size, percentage, section, 
StorageItemPosition.PENULTIMATE);
+        }
+
+        public StorageItem.storage(string name, string path, uint64 size, int section = 0)
+        {
+            StorageItem.item(StorageItemType.STORAGE, name, path, size, 0, section, 
StorageItemPosition.FIRST);
+        }
+
+        public StorageItem.system(string name, uint64 size, double percentage, int section = 0)
+        {
+            StorageItem.item(StorageItemType.SYSTEM, name, "", size, percentage, section);
+        }
+
+        public StorageItem.available(uint64 size, double percentage, int section = 0)
+        {
+            StorageItem.item(StorageItemType.AVAILABLE, _("Available"), "", size, percentage, section, 
StorageItemPosition.LAST);
+        }
+
+        public StorageItemPosition get_prefered_position()
+        {
+            return prefered_position;
+        }
+
+        public void set_color(Gdk.RGBA color)
+        {
+            this.color = color;
+        }
+
+        public Gdk.RGBA get_color()
+        {
+            return color;
+        }
+
+        public int get_section()
+        {
+            return section;
+        }
+
+        public uint64 get_size()
+        {
+            return size;
+        }
+
+        public double get_percentage()
+        {
+            return percentage;
+        }
+
+        public string get_name()
+        {
+            return name;
+        }
+
+        public StorageItemType get_item_type()
+        {
+            return type;
+        }
+
+        public string get_path()
+        {
+            return path;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/storage-list-box.vala b/src/storage-list-box.vala
new file mode 100644
index 0000000..bcedbae
--- /dev/null
+++ b/src/storage-list-box.vala
@@ -0,0 +1,140 @@
+namespace Usage
+{
+    public class StorageListBox : Gtk.ListBox
+    {
+        public signal void loading();
+        public signal void loaded();
+
+        private List<string?> path_history;
+        private List<string?> name_history;
+        private string? actual_path;
+        private string? actual_name;
+        private ListStore model;
+        private bool root = true;
+        private StorageAnalyzer storage_analyzer;
+        private Gdk.RGBA color;
+
+        public StorageListBox()
+        {
+            set_selection_mode (Gtk.SelectionMode.NONE);
+            set_header_func (update_header);
+
+            model = new ListStore(typeof(StorageItem));
+            bind_model(model, on_row_created);
+
+            row_activated.connect(on_row_activated);
+
+            path_history = new List<string>();
+            name_history = new List<string>();
+            actual_path = null;
+            actual_name = null;
+            storage_analyzer = (GLib.Application.get_default() as Application).get_storage_analyzer();
+            reload();
+        }
+
+        public void on_back_button_clicked()
+        {
+            unowned List<string>? path = path_history.last();
+            unowned List<string>? name = name_history.last();
+            actual_path = path.data;
+            actual_name = name.data;
+            load(path.data);
+            path_history.delete_link(path);
+            name_history.delete_link(name);
+            if(root)
+            {
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_back_button(false);
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_stack_switcher();
+            }
+            else
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_title_text(actual_name);
+        }
+
+        public void reload()
+        {
+            get_style_context().add_class("folders");
+            color = get_style_context().get_color(get_style_context().get_state());
+            get_style_context().remove_class("folders");
+            this.hide();
+            loading();
+            root = true;
+            storage_analyzer.cache_complete.connect(() => {
+                storage_analyzer.prepare_items.begin(actual_path, color, (obj, res) => {
+                    loaded();
+                    this.show();
+                    model.remove_all();
+
+                    foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
+                        model.append(item);
+                });
+            });
+        }
+
+        private void load(string? path)
+        {
+            if(path == null)
+            {
+                root = true;
+                get_style_context().add_class("folders");
+                color = get_style_context().get_color(get_style_context().get_state());
+                get_style_context().remove_class("folders");
+            }
+            else
+                root = false;
+
+            this.hide();
+            loading();
+            storage_analyzer.prepare_items.begin(path, color, (obj, res) => {
+                loaded();
+                this.show();
+                model.remove_all();
+
+                foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
+                    model.append(item);
+            });
+        }
+
+        private void on_row_activated (Gtk.ListBoxRow row)
+        {
+            StorageRow storage_row = (StorageRow) row;
+            if(storage_row.get_item_type() == StorageItemType.FILE)
+                ;//TODO: action on click
+            else if(storage_row.get_item_type() != StorageItemType.STORAGE && storage_row.get_item_type() != 
StorageItemType.SYSTEM
+                 && storage_row.get_item_type() != StorageItemType.AVAILABLE)
+            {
+                path_history.append(actual_path);
+                name_history.append(actual_name);
+                actual_path = storage_row.get_item_path();
+                actual_name = storage_row.get_item_name();
+
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_back_button(true);
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_title_text(actual_name);
+
+                if(root)
+                    color = storage_row.get_color();
+
+                load(actual_path);
+            }
+        }
+
+        private Gtk.Widget on_row_created (Object item)
+        {
+            unowned StorageItem storage_item = (StorageItem) item;
+            var row = new StorageRow(storage_item);
+            return row;
+        }
+
+        private void update_header(Gtk.ListBoxRow row, Gtk.ListBoxRow? before_row)
+        {
+               if(before_row == null)
+                row.set_header(null);
+            else
+            {
+                var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
+                separator.get_style_context().add_class("list");
+               separator.show();
+                   row.set_header(separator);
+               }
+        }
+    }
+}
diff --git a/src/storage-row.vala b/src/storage-row.vala
new file mode 100644
index 0000000..d083ffd
--- /dev/null
+++ b/src/storage-row.vala
@@ -0,0 +1,129 @@
+using Posix;
+using Gee;
+
+namespace Usage
+{
+    public class StorageRow : Gtk.ListBoxRow
+    {
+        private StorageItemType type;
+        private string item_path;
+        private string item_name;
+        private Gdk.RGBA color;
+
+        public StorageRow(StorageItem storage_item)
+        {
+            var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
+            box.margin = 10;
+            box.margin_top = 12;
+            box.margin_bottom = 12;
+            var size_label = new Gtk.Label(Utils.format_size_values(storage_item.get_size()));
+            item_path = storage_item.get_path();
+            item_name = storage_item.get_name();
+            type = storage_item.get_item_type();
+
+            string? color_css_class = null;
+            bool is_header = false;
+
+            ColorRectangle color_rectangle;
+
+            switch(storage_item.get_item_type())
+            {
+                case StorageItemType.SYSTEM:
+                    color_css_class = "system";
+                    color_rectangle = new ColorRectangle.new_from_css(color_css_class);
+                    color = color_rectangle.get_color();
+                    box.pack_start(color_rectangle, false, false, 5);
+                    break;
+                case StorageItemType.TRASH:
+                    color_css_class = "trash";
+                    color_rectangle = new ColorRectangle.new_from_css(color_css_class);
+                    color = color_rectangle.get_color();
+                    box.pack_start(color_rectangle, false, false, 5);
+                    break;
+                case StorageItemType.USER:
+                    color_css_class = "user";
+                    color_rectangle = new ColorRectangle.new_from_css(color_css_class);
+                    color = color_rectangle.get_color();
+                    box.pack_start(color_rectangle, false, false, 5);
+                    break;
+                case StorageItemType.AVAILABLE:
+                    color_css_class = "available-storage";
+                    color_rectangle = new ColorRectangle.new_from_css(color_css_class);
+                    color = color_rectangle.get_color();
+                    box.pack_start(color_rectangle, false, false, 5);
+                    break;
+                case StorageItemType.STORAGE:
+                    is_header = true;
+                    box.margin_top = 9;
+                    box.margin_bottom = 9;
+                    break;
+                case StorageItemType.DOCUMENTS:
+                case StorageItemType.DOWNLOADS:
+                case StorageItemType.DESKTOP:
+                case StorageItemType.MUSIC:
+                case StorageItemType.PICTURES:
+                case StorageItemType.VIDEOS:
+                    get_style_context().add_class("folders");
+                    color = get_style_context().get_color(get_style_context().get_state());
+                    get_style_context().remove_class("folders");
+                    color_rectangle = new ColorRectangle.new_from_rgba(storage_item.get_color());
+                    box.pack_start(color_rectangle, false, false, 5);
+                    break;
+                case StorageItemType.DIRECTORY:
+                    color = storage_item.get_color();
+                    var info = Gtk.IconTheme.get_default().lookup_icon("folder-symbolic", 15, 0);
+
+                    try {
+                        var pixbuf = info.load_symbolic (storage_item.get_color());
+                        var icon = new Gtk.Image.from_pixbuf (pixbuf);
+                        box.pack_start(icon, false, false, 5);
+                    }
+                    catch(Error e) {
+                        GLib.stderr.printf ("Could not load folder-symbolic icon: %s\n", e.message);
+                    }
+                    break;
+                 case StorageItemType.FILE:
+                    color = storage_item.get_color();
+                    var info = Gtk.IconTheme.get_default().lookup_icon("folder-documents-symbolic", 15, 0);
+
+                    try {
+                        var pixbuf = info.load_symbolic (storage_item.get_color());
+                        var icon = new Gtk.Image.from_pixbuf (pixbuf);
+                        box.pack_start(icon, false, false, 5);
+                    }
+                    catch(Error e) {
+                        GLib.stderr.printf ("Could not load folder-documents-symbolic icon: %s\n", 
e.message);
+                    }
+                    break;
+            }
+
+            var title_label = new Gtk.Label(storage_item.get_name());
+            title_label.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
+            box.pack_start(title_label, false, true, 5);
+            box.pack_end(size_label, false, true, 10);
+            add(box);
+
+            show_all();
+        }
+
+        public Gdk.RGBA get_color()
+        {
+            return color;
+        }
+
+        public string get_item_name()
+        {
+            return item_name;
+        }
+
+        public string get_item_path()
+        {
+            return item_path;
+        }
+
+        public StorageItemType get_item_type()
+        {
+            return type;
+        }
+    }
+}
diff --git a/src/storage-view.vala b/src/storage-view.vala
index d036784..f531c67 100644
--- a/src/storage-view.vala
+++ b/src/storage-view.vala
@@ -2,10 +2,48 @@ namespace Usage
 {
     public class StorageView : View
     {
+        private StorageListBox storage_list_box;
+
         public StorageView ()
         {
             name = "STORAGE";
             title = _("Storage");
+
+            var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+            storage_list_box = new StorageListBox();
+            var scrolled_window = new Gtk.ScrolledWindow(null, null);
+            scrolled_window.add(box);
+
+            var spinner = new Gtk.Spinner();
+            spinner.active = true;
+            spinner.margin_top = 30;
+            spinner.margin_bottom = 20;
+
+            box.pack_start(spinner, true, true, 0);
+            box.pack_start(storage_list_box, false, false, 0);
+
+            storage_list_box.loading.connect(() =>
+            {
+                spinner.show();
+            });
+
+            storage_list_box.loaded.connect(() =>
+            {
+                spinner.hide();
+            });
+
+            var graph = new StorageGraph();
+
+            var paned = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
+            paned.position = 300;
+            paned.add1(scrolled_window);
+            paned.add(graph);
+            add(paned);
+        }
+
+        public StorageListBox get_storage_list_box()
+        {
+            return storage_list_box;
         }
     }
 }
diff --git a/src/storage-worker.vala b/src/storage-worker.vala
new file mode 100644
index 0000000..f51e15f
--- /dev/null
+++ b/src/storage-worker.vala
@@ -0,0 +1,87 @@
+namespace Usage
+{
+    [Compact]
+    public class StorageResult {
+        internal string path;
+        internal uint64 size;
+        internal float percentage;
+    }
+
+    public class StorageWorker
+    {
+        private AsyncQueue<StorageResult> results_queue;
+        private File path;
+        private string[] exclude_paths;
+        private Cancellable cancellable;
+        private string? name;
+        private uint64 total_size;
+
+        public StorageWorker(File path, uint64 total_size, ref Cancellable cancellable, ref 
AsyncQueue<StorageResult> results_queue, string? name = null, string[]? exclude_paths = null)
+        {
+            this.path = path;
+            this.cancellable = cancellable;
+            this.name = name;
+            this.exclude_paths = exclude_paths;
+            this.results_queue = results_queue;
+            this.total_size = total_size;
+        }
+
+        private StorageResult get_directory(File file, out uint64 size)
+        {
+            string path = file.get_parse_name();
+            FileEnumerator enumerator;
+            List<StorageResult> childs = new List<StorageResult>();
+            size = 0;
+
+            try {
+                enumerator = file.enumerate_children(FileAttribute.STANDARD_SIZE, 
FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+                FileInfo info;
+
+                while((info = enumerator.next_file(null)) != null && cancellable.is_cancelled() == false)
+                {
+                    if(info.get_file_type() == FileType.DIRECTORY)
+                    {
+                        if(exclude_paths == null || !(file.resolve_relative_path(info.get_name()).get_path() 
in exclude_paths))
+                        {
+                            var child = file.get_child(info.get_name());
+                            uint64 child_size = 0;
+                            childs.append(get_directory(child, out child_size));
+                            size += child_size;
+                        }
+                    }
+                    else if(info.get_file_type() == FileType.REGULAR)
+                    {
+                        size += info.get_size();
+                    }
+                }
+            }
+            catch (Error e) {
+                stderr.printf ("Error: %s\n", e.message);
+            }
+
+            StorageResult result = new StorageResult();
+            result.path = path;
+            result.size = size;
+
+            foreach (unowned StorageResult child in childs)
+            {
+                child.percentage = ((float) child.size / size) * 100;
+                var child_copy = new StorageResult();
+                child_copy.path = child.path;
+                child_copy.size = child.size;
+                child_copy.percentage = child.percentage;
+                results_queue.push ((owned) child_copy);
+            }
+
+            return result;
+        }
+
+        public void run ()
+        {
+            uint64 size = 0;
+            var child = get_directory(path, out size);
+            child.percentage = ((float) size / total_size) * 100;
+            results_queue.push ((owned) child);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/utils.vala b/src/utils.vala
index 37f7138..f4cdb61 100644
--- a/src/utils.vala
+++ b/src/utils.vala
@@ -31,5 +31,35 @@ namespace Usage
             else
                 return value.to_string() + " B/s";
         }
+
+        public static Gdk.RGBA color_darker(Gdk.RGBA color, double percentage)
+        {
+            color.red = color_field_darker(color.red, percentage);
+            color.green = color_field_darker(color.green, percentage);
+            color.blue = color_field_darker(color.blue, percentage);
+
+            return color;
+        }
+
+        public static Gdk.RGBA color_lighter(Gdk.RGBA color, double percentage)
+        {
+            color.red = color_field_lighter(color.red, percentage);
+            color.green = color_field_lighter(color.green, percentage);
+            color.blue = color_field_lighter(color.blue, percentage);
+
+            return color;
+        }
+
+        private static double color_field_darker(double field, double percentage)
+        {
+            field = field * 255;
+            return (field - ((field / 100) * percentage)) / 255;
+        }
+
+        private static double color_field_lighter(double field, double percentage)
+        {
+            field = field * 255;
+            return (field + (((255 - field) / 100) * percentage)) / 255;
+        }
     }
 }
diff --git a/src/vapis/glibtop.vapi b/src/vapis/glibtop.vapi
index 41d9feb..b55250e 100644
--- a/src/vapis/glibtop.vapi
+++ b/src/vapis/glibtop.vapi
@@ -134,7 +134,7 @@ namespace GTop {
         uint32 number;
     }
 
-    [CCode(array_length = false, array_null_terminated = false)]
+    [CCode(array_null_terminated = "true")]
     public string[] get_netlist(out Netlist netlist);
 
     [CCode(cname = "glibtop_proc_uid", cheader_filename = "glibtop/procuid.h")]
@@ -168,4 +168,38 @@ namespace GTop {
     }
     [CCode(array_null_terminated = "true")]
     public string[] get_proc_argv(out ProcArgs proc_args, Posix.pid_t pid);
+
+    [CCode(cname = "glibtop_fsusage", cheader_filename = "glibtop/fsusage.h")]
+    public struct FsUsage {
+        uint64 flags;
+        uint64 blocks;
+        uint64 bfree;
+        uint64 bavail;
+        uint64 files;
+        uint64 ffree;
+        uint32 block_size;
+        uint64 read;
+        uint64 write;
+    }
+    public void get_fsusage(out FsUsage fs_usage, string mount_dir);
+
+    [CCode(cname = "glibtop_mountentry", cheader_filename = "glibtop/mountlist.h", 
destroy_function="g_free")]
+    public struct MountEntry
+    {
+       uint64 dev;
+       char devname[80];
+       char mountdir[80];
+       char type[80];
+    }
+
+    [CCode(cname = "glibtop_mountlist", cheader_filename = "glibtop/mountlist.h")]
+    public struct MountList
+    {
+       uint64 flags;
+       uint64 number;
+       uint64 total;
+       uint64 size;
+    }
+    [CCode(array_length = false)]
+    public MountEntry[] get_mountlist(out MountList mount_list, bool all_fs);
 }
diff --git a/src/window.vala b/src/window.vala
index 5a1c125..238e5db 100644
--- a/src/window.vala
+++ b/src/window.vala
@@ -2,8 +2,8 @@ namespace Usage
 {
     public class Window : Gtk.ApplicationWindow
     {
-        private Gtk.Stack stack;
         private Usage.HeaderBar header_bar;
+        private View[] views;
 
                public Window(Gtk.Application application)
         {
@@ -20,13 +20,11 @@ namespace Usage
                 load_css();
             });
 
-                       stack = new Gtk.Stack();
+                       var stack = new Gtk.Stack();
                        header_bar = new Usage.HeaderBar(stack);
                        set_titlebar(header_bar);
 
-                       header_bar.show_close_button = true;
-
-            var views = new View[]
+            views = new View[]
             {
                 new PerformanceView(),
                 new DataView(),
@@ -37,9 +35,39 @@ namespace Usage
             foreach(var view in views)
                 stack.add_titled(view, view.name, view.title);
 
+            stack.notify.connect(() => {
+                if(stack.visible_child_name == views[0].name)
+                {
+                    header_bar.set_mode(HeaderBarMode.PERFORMANCE);
+                }
+                else if(stack.visible_child_name == views[1].name)
+                {
+                    header_bar.set_mode(HeaderBarMode.DATA);
+                }
+                else if(stack.visible_child_name == views[2].name)
+                {
+                    header_bar.set_mode(HeaderBarMode.STORAGE);
+                    (GLib.Application.get_default() as 
Application).get_storage_analyzer().create_cache.begin();
+                }
+                else if(stack.visible_child_name == views[3].name)
+                {
+                    header_bar.set_mode(HeaderBarMode.POWER);
+                }
+            });
+
             this.add(stack);
         }
 
+        public Usage.HeaderBar get_header_bar()
+        {
+            return header_bar;
+        }
+
+        public View[] get_views()
+        {
+            return views;
+        }
+
         private void load_css()
         {
             string name_css = "adwaita.css";


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