[gnome-usage] Add multiselect support in storage.



commit e5919980f2d0edbf0e5902e125c6cc2ab418a365
Author: Petr Štětka <pstetka redhat com>
Date:   Thu Apr 6 14:03:06 2017 +0200

    Add multiselect support in storage.

 README.md                  |    6 +-
 po/POTFILES.in             |    1 +
 src/application.vala       |    3 +-
 src/gnome-usage.vala       |    7 +
 src/header-bar.vala        |  126 ++++++++++++++++++++-
 src/meson.build            |    1 +
 src/storage-actionbar.vala |  276 ++++++++++++++++++++++++++++++++++++++++++++
 src/storage-analyzer.vala  |    2 +-
 src/storage-list-box.vala  |  166 ++++++++++++++++++++++++++-
 src/storage-row.vala       |  140 ++++++++++++++--------
 src/storage-view.vala      |   25 ++++-
 11 files changed, 690 insertions(+), 63 deletions(-)
---
diff --git a/README.md b/README.md
index da0f6d9..1c937d2 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,11 @@ New GNOME Usage!
 - [x] Memory usage
 - [x] Network usage
 - [x] Search in processes 
-- [ ] Multiselection in storage
 - [ ] UI for file operations errors (as duplicate file, not enough space, permission...)
-- [ ] Notification for file operations and rollback
+- [ ] UI for show progress about file operations
+- [ ] Notification for file operations with undo action
 - [ ] Storage support for more users (multiuser system)
-- [ ] Application section in storage 
+- [ ] Application in storage 
 - [ ] Power view (Design?)
 - [ ] Disk usage (What library we can use?)
 - [ ] Data view - 0%
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 78ff036..25ba2da 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -12,6 +12,7 @@ src/network-sub-view.vala
 src/performance-view.vala
 src/power-view.vala
 src/process-dialog.vala
+src/storage-actionbar.vala
 src/storage-analyzer.vala
 src/storage-item.vala
 src/storage-row.vala
diff --git a/src/application.vala b/src/application.vala
index 672c5a8..3bba4d1 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -79,7 +79,8 @@ namespace Usage
                translator_credits: _("translator-credits"),
                website: "https://wiki.gnome.org/Apps/Usage";,
                website_label: _("Websites"),
-               version: Config.VERSION,
+               version: Config.VERSION, //messon
+               //version: Constants.VERSION, //autovala
                license_type: License.GPL_3_0);
         }
 
diff --git a/src/gnome-usage.vala b/src/gnome-usage.vala
index 81381ff..373aaf5 100644
--- a/src/gnome-usage.vala
+++ b/src/gnome-usage.vala
@@ -1,10 +1,17 @@
 public static int main (string[] args)
 {
+    // messon
     Intl.bindtextdomain(Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR);
     Intl.setlocale(LocaleCategory.ALL, "");
     Intl.textdomain(Config.GETTEXT_PACKAGE);
     Intl.bind_textdomain_codeset(Config.GETTEXT_PACKAGE, "utf-8");
 
+    //Autovala
+    /*Intl.bindtextdomain(Constants.GETTEXT_PACKAGE, Path.build_filename(Constants.DATADIR,"locale"));
+    Intl.setlocale(LocaleCategory.ALL, "");
+    Intl.textdomain(Constants.GETTEXT_PACKAGE);
+    Intl.bind_textdomain_codeset(Constants.GETTEXT_PACKAGE, "utf-8");*/
+
     var application = new Usage.Application();
     return application.run(args);
 }
diff --git a/src/header-bar.vala b/src/header-bar.vala
index f1b6e71..0402a15 100644
--- a/src/header-bar.vala
+++ b/src/header-bar.vala
@@ -15,11 +15,20 @@ namespace Usage
            private bool active_performance_search_btn = false;
            private Gtk.Button? storage_back_button;
            private Gtk.Button? storage_rescan_button;
+           private Gtk.Button? storage_select_button;
+           private Gtk.Button? storage_cancel_button;
+           private Gtk.MenuButton? storage_selection_menu;
            private bool show_storage_back_btn = false;
            private bool show_storage_rescan_btn = false;
+           private bool show_storage_select_btn = false;
            private string title_text = "";
            private HeaderBarMode mode;
 
+           const GLib.ActionEntry[] select_action_entries = {
+           { "select-all", select_all },
+           { "select-none", select_none },
+        };
+
            public HeaderBar(Gtk.Stack stack)
            {
                mode = HeaderBarMode.PERFORMANCE;
@@ -45,8 +54,13 @@ namespace Usage
                 case HeaderBarMode.STORAGE:
                     remove_widget(storage_back_button);
                     remove_widget(storage_rescan_button);
+                    remove_widget(storage_select_button);
+                    remove_widget(storage_cancel_button);
                     storage_rescan_button = null;
                     storage_back_button = null;
+                    storage_select_button = null;
+                    storage_select_button = null;
+                    storage_cancel_button = null;
                     break;
                 case HeaderBarMode.POWER:
                     break;
@@ -83,15 +97,29 @@ namespace Usage
                     storage_rescan_button = new Gtk.Button.from_icon_name("view-refresh-symbolic");
                     storage_rescan_button.clicked.connect(() => {
                         show_stack_switcher();
+                        show_storage_select_button(false);
                         show_storage_rescan_button(false);
                         show_storage_back_button(false);
                         (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_select_button = new Gtk.Button.from_icon_name("emblem-ok-symbolic");
+                    storage_select_button.clicked.connect(() => {
+                        show_storage_selection_mode(true);
+                    });
+                    show_storage_select_button(show_storage_select_btn);
                     show_storage_rescan_button(show_storage_rescan_btn);
 
+                    storage_cancel_button = new Gtk.Button.with_label(_("Cancel"));
+                    storage_cancel_button.clicked.connect(() => {
+                        show_storage_selection_mode(false);
+                    });
+
                     pack_start(storage_back_button);
+                    pack_end(storage_select_button);
                     pack_end(storage_rescan_button);
+                    pack_end(storage_cancel_button);
                     break;
                 case HeaderBarMode.POWER:
                     show_stack_switcher();
@@ -100,10 +128,15 @@ namespace Usage
             this.mode = mode;
            }
 
-           private void remove_widget(Gtk.Widget? widget)
+           public void change_selected_items(uint count)
            {
-            if(widget != null)
-                remove(widget);
+               if(storage_selection_menu != null)
+               {
+                   if(count > 0)
+                    storage_selection_menu.label = ngettext ("%u selected", "%u selected", count).printf 
(count);
+                else
+                    storage_selection_menu.label = _("Click on items to select them");
+               }
            }
 
            public HeaderBarMode get_mode()
@@ -159,6 +192,22 @@ namespace Usage
             }
         }
 
+        public void show_storage_select_button(bool show)
+        {
+            if(show)
+            {
+                if(storage_select_button != null)
+                    storage_select_button.show();
+                show_storage_select_btn = true;
+            }
+            else
+            {
+                if(storage_select_button != null)
+                    storage_select_button.hide();
+                show_storage_select_btn = false;
+            }
+        }
+
         public void action_on_search()
         {
             switch(mode)
@@ -172,5 +221,76 @@ namespace Usage
                     break;
             }
         }
+
+        public void show_storage_selection_mode(bool show)
+        {
+            if(show)
+            {
+                show_storage_rescan_button(false);
+                show_storage_select_button(false);
+                storage_back_button.hide();
+                storage_cancel_button.show();
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).show_action_bar(true);
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().set_select_mode(true);
+
+                var menu = new GLib.Menu ();
+                var item = new GLib.MenuItem (_("Select all"), "headerbar.select-all");
+                item.set_attribute("accel", "s", "<Primary>a");
+                menu.append_item(item);
+
+                item = new GLib.MenuItem (_("Select None"), "headerbar.select-none");
+                menu.append_item(item);
+
+                storage_selection_menu = new Gtk.MenuButton();
+                storage_selection_menu.get_style_context().add_class("selection-menu");
+                storage_selection_menu.set_menu_model(menu);
+
+                var action_group = new GLib.SimpleActionGroup ();
+                action_group.add_action_entries (select_action_entries, this);
+                storage_selection_menu.get_popover().insert_action_group ("headerbar", action_group);
+
+                storage_selection_menu.show();
+                set_custom_title(storage_selection_menu);
+                change_selected_items(0);
+
+                this.get_style_context().add_class("selection-mode");
+                this.show_close_button = false;
+            }
+            else
+            {
+                if(show_storage_back_btn)
+                    storage_back_button.show();
+
+                show_storage_rescan_button(true);
+                show_storage_select_button(true);
+                storage_cancel_button.hide();
+                storage_selection_menu = null;
+                if(title_text == "")
+                    show_stack_switcher();
+                else
+                    show_title();
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).show_action_bar(false);
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().set_select_mode(false);
+                this.get_style_context().remove_class("selection-mode");
+                this.show_close_button = true;
+            }
+        }
+
+        private void select_all()
+        {
+            ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().select_all_rows();
+
+        }
+
+        private void select_none()
+        {
+            ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().unselect_all_rows();
+        }
+
+        private void remove_widget(Gtk.Widget? widget)
+        {
+            if(widget != null)
+                remove(widget);
+        }
        }
 }
\ No newline at end of file
diff --git a/src/meson.build b/src/meson.build
index b1639bf..b09a7f5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -31,6 +31,7 @@ vala_sources = [
     'process-row.vala',
     'process.vala',
     'settings.vala',
+    'storage-actionbar.vala',
     'storage-analyzer.vala',
     'storage-graph.vala',
     'storage-item.vala',
diff --git a/src/storage-actionbar.vala b/src/storage-actionbar.vala
new file mode 100644
index 0000000..a8594cf
--- /dev/null
+++ b/src/storage-actionbar.vala
@@ -0,0 +1,276 @@
+namespace Usage
+{
+       public class StorageActionBar : Gtk.ActionBar
+       {
+           private Gtk.Button move_to_button;
+           private Gtk.Button delete_button;
+           private Gtk.Button move_to_trash_button;
+           private Gtk.Button empty_folder_button;
+           private Gtk.Button restore_button;
+           private Gtk.Button delete_from_trash_button;
+
+        public StorageActionBar()
+        {
+            //common
+            move_to_button = new Gtk.Button.with_label(_("Move to"));
+            move_to_button.clicked.connect(move_to_clicked);
+            delete_button = new Gtk.Button.with_label(_("Delete"));
+            delete_button.clicked.connect(delete_clicked);
+            delete_button.get_style_context().add_class ("destructive-action");
+            move_to_trash_button = new Gtk.Button.with_label(_("Move to Trash"));
+            move_to_trash_button.clicked.connect(move_to_trash_clicked);
+            this.pack_start(move_to_button);
+            this.pack_end(delete_button);
+            this.pack_end(move_to_trash_button);
+
+            //root
+            empty_folder_button = new Gtk.Button.with_label(_("Empty folder"));
+            empty_folder_button.clicked.connect(empty_folder_clicked);
+            this.pack_end(empty_folder_button);
+
+            //trash
+            restore_button = new Gtk.Button.with_label(_("Restore"));
+            restore_button.clicked.connect(restore_clicked);
+            delete_from_trash_button = new Gtk.Button.with_label(_("Delete from Trash"));
+            delete_from_trash_button.clicked.connect(delete_from_trash_clicked);
+            delete_from_trash_button.get_style_context().add_class ("destructive-action");
+            this.pack_start(restore_button);
+            this.pack_end(delete_from_trash_button);
+
+            hide_all();
+        }
+
+        public void show_common()
+        {
+            hide_all();
+            move_to_button.visible = true;
+            delete_button.visible = true;
+            move_to_trash_button.visible = true;
+        }
+
+        public void show_root()
+        {
+            hide_all();
+            empty_folder_button.visible = true;
+        }
+
+        public void show_trash()
+        {
+            hide_all();
+            restore_button.visible = true;
+            delete_from_trash_button.visible = true;
+        }
+
+        public void hide_all()
+        {
+            move_to_button.set_visible(false);
+            delete_button.set_visible(false);
+            move_to_trash_button.set_visible(false);
+            empty_folder_button.set_visible(false);
+            restore_button.set_visible(false);
+            delete_from_trash_button.set_visible(false);
+        }
+
+        public void set_sensitive_all(bool sensitive)
+        {
+            move_to_button.set_sensitive(sensitive);
+            delete_button.set_sensitive(sensitive);
+            move_to_trash_button.set_sensitive(sensitive);
+            empty_folder_button.set_sensitive(sensitive);
+            restore_button.set_sensitive(sensitive);
+            delete_from_trash_button.set_sensitive(sensitive);
+        }
+
+        private void move_to_clicked()
+        {
+            Gtk.FileChooserDialog chooser = new Gtk.FileChooserDialog (
+                _("Select destination folder"), (GLib.Application.get_default() as 
Application).get_window(), Gtk.FileChooserAction.SELECT_FOLDER,
+                _("Cancel"),
+                Gtk.ResponseType.CANCEL,
+                _("Select"),
+                Gtk.ResponseType.ACCEPT);
+            chooser.destroy_with_parent = true;
+            Gtk.FileFilter filter = new Gtk.FileFilter();
+            filter.add_custom(Gtk.FileFilterFlags.FILENAME, (filter_info) => {
+                foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                {
+                    StorageRow storage_row = (StorageRow) row;
+                    if(filter_info.filename == storage_row.get_item_path())
+                        return false;
+                }
+                return true;
+            });
+            chooser.set_filter(filter);
+            StorageRow storage_row = (StorageRow) ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_row();
+            chooser.set_filename(storage_row.get_item_path());
+            chooser.show();
+
+            if(chooser.run() == Gtk.ResponseType.ACCEPT)
+            {
+               Timeout.add(0, () => {
+                    foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                    {
+                        StorageRow actual_storage_row = (StorageRow) row;
+                        string destination = chooser.get_file().get_parse_name() + "/" + 
Path.get_basename(actual_storage_row.get_item_path());
+                        var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                        
storage_analyzer.move_file.begin(File.new_for_path(actual_storage_row.get_item_path()), 
File.new_for_path(destination), () => {
+                            ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                        });
+                    }
+                    hide_selection_mode();
+
+                    return false;
+                });
+            }
+            chooser.destroy();
+        }
+
+        private void delete_clicked()
+        {
+            string files = "";
+            foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+            {
+                StorageRow storage_row = (StorageRow) row;
+                if(files != "")
+                    files += ", ";
+                files += storage_row.get_item_name();
+            }
+
+            var dialog = new Gtk.MessageDialog ((GLib.Application.get_default() as 
Application).get_window(), Gtk.DialogFlags.MODAL,
+                Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("Are you sure you want to permanently 
delete this items %s?").printf(files));
+            dialog.secondary_text = _("If you delete these items, they will be permanently lost.");
+
+            if(dialog.run() == Gtk.ResponseType.OK)
+            {
+               Timeout.add(0, () => {
+                    foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                    {
+                        StorageRow storage_row = (StorageRow) row;
+                        var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                        storage_analyzer.delete_file.begin(storage_row.get_item_path(), () => {
+                            ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                        });
+                    }
+                    hide_selection_mode();
+
+                    return false;
+                });
+            }
+            dialog.destroy();
+
+
+        }
+
+        private void move_to_trash_clicked()
+        {
+            Timeout.add(0, () => {
+                foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                {
+                    StorageRow storage_row = (StorageRow) row;
+                    var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                    storage_analyzer.trash_file.begin(storage_row.get_item_path(), () => {
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                    });
+                }
+                hide_selection_mode();
+
+                return false;
+            });
+        }
+
+        private void empty_folder_clicked()
+        {
+            Timeout.add(0, () => {
+                string folders = "";
+                foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                {
+                    StorageRow storage_row = (StorageRow) row;
+                    if(storage_row.get_item_type() == StorageItemType.TRASH)
+                    {
+                        var dialog = new Gtk.MessageDialog ((GLib.Application.get_default() as 
Application).get_window(), Gtk.DialogFlags.MODAL,
+                            Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("Empty all items from 
Trash?"));
+                        dialog.secondary_text = _("All items in the Trash will be permanently deleted.");
+
+                        if(dialog.run() == Gtk.ResponseType.OK)
+                        {
+                            var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                            storage_analyzer.wipe_trash.begin(() => {
+                                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                            });
+                        }
+                        dialog.destroy();
+                    }
+                    else
+                    {
+                        if(folders != "")
+                            folders += ", ";
+                        folders += storage_row.get_item_name();
+                    }
+                }
+
+                if(folders != "")
+                {
+                    var dialog = new Gtk.MessageDialog ((GLib.Application.get_default() as 
Application).get_window(), Gtk.DialogFlags.MODAL,
+                        Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("Empty all items from 
%s?").printf(folders));
+                    dialog.secondary_text = _("All items in the %s will be moved to the 
Trash.").printf(folders);
+
+                    if(dialog.run() == Gtk.ResponseType.OK)
+                    {
+                        foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                        {
+                            StorageRow storage_row = (StorageRow) row;
+                            var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                            storage_analyzer.wipe_folder.begin(storage_row.get_item_path(), () => {
+                                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                            });
+                        }
+
+                    }
+                    dialog.destroy();
+                }
+                hide_selection_mode();
+
+                return false;
+            });
+        }
+
+        private void restore_clicked()
+        {
+            Timeout.add(0, () => {
+                foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                {
+                    StorageRow storage_row = (StorageRow) row;
+                    var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                    storage_analyzer.restore_trash_file.begin(storage_row.get_item_path(), () => {
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                    });
+                }
+                hide_selection_mode();
+
+                return false;
+            });
+        }
+
+        private void delete_from_trash_clicked()
+        {
+            Timeout.add(0, () => {
+                foreach (Gtk.ListBoxRow row in ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().get_selected_rows())
+                {
+                    StorageRow storage_row = (StorageRow) row;
+                    var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                    storage_analyzer.delete_trash_file.begin(storage_row.get_item_path(), () => {
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                    });
+                }
+                hide_selection_mode();
+
+                return false;
+            });
+        }
+
+        private void hide_selection_mode()
+        {
+            (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_selection_mode(false);
+        }
+       }
+}
\ No newline at end of file
diff --git a/src/storage-analyzer.vala b/src/storage-analyzer.vala
index 2cbb58f..e81add7 100644
--- a/src/storage-analyzer.vala
+++ b/src/storage-analyzer.vala
@@ -760,7 +760,7 @@ namespace Usage
                     GTop.get_fsusage(out root, mountdir);
 
                     Storage storage = Storage();
-                    storage.free = root.bfree * root.block_size; //TODO bavail or bfree
+                    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;
diff --git a/src/storage-list-box.vala b/src/storage-list-box.vala
index be13daf..a4e42a4 100644
--- a/src/storage-list-box.vala
+++ b/src/storage-list-box.vala
@@ -26,6 +26,7 @@ namespace Usage
             bind_model(model, on_row_created);
 
             row_activated.connect(on_row_activated);
+            selected_rows_changed.connect(on_selected_rows_changed);
 
             path_history = new List<string>();
             name_history = new List<string>();
@@ -75,6 +76,10 @@ namespace Usage
             {
                 (GLib.Application.get_default() as 
Application).get_window().get_header_bar().set_title_text(actual_name);
                 (GLib.Application.get_default() as Application).get_window().get_header_bar().show_title();
+                if(actual_parent_type == StorageItemType.TRASHFILE || actual_parent_type == 
StorageItemType.TRASHSUBFILE)
+                    (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_select_button(false);
+                else
+                    (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_select_button(true);
             }
         }
 
@@ -96,6 +101,9 @@ namespace Usage
                     }
 
                     header_bar.show_storage_rescan_button(true);
+                    if(actual_parent_type != StorageItemType.TRASHFILE && actual_parent_type != 
StorageItemType.TRASHSUBFILE)
+                        (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_select_button(true);
+
                     loaded();
                     this.show();
                     model.remove_all();
@@ -114,6 +122,79 @@ namespace Usage
             load(actual_path, actual_parent_type);
         }
 
+        public void set_select_mode(bool select_mode)
+        {
+            if(select_mode)
+            {
+                set_selection_mode (Gtk.SelectionMode.MULTIPLE);
+                this.forall ((child) => {
+                    var row = child as StorageRow;
+                    if (row == null)
+                        return;
+
+                    row.set_show_check_button(true);
+                });
+
+                if(root)
+                    ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_action_bar().show_root();
+                else if(actual_parent_type == StorageItemType.TRASH)
+                    ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_action_bar().show_trash();
+                else if(actual_parent_type != StorageItemType.TRASHFILE && actual_parent_type != 
StorageItemType.TRASHSUBFILE)
+                    ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_action_bar().show_common();
+
+                on_selected_rows_changed();
+            }
+            else
+            {
+                set_selection_mode (Gtk.SelectionMode.NONE);
+                this.forall ((child) => {
+                    var row = child as StorageRow;
+                    if (row == null)
+                        return;
+
+                    row.set_show_check_button(false);
+                });
+            }
+        }
+
+        public bool get_select_mode()
+        {
+            if(get_selection_mode() == Gtk.SelectionMode.MULTIPLE)
+                return true;
+            else
+                return false;
+        }
+
+        public void select_all_rows()
+        {
+            if(get_select_mode())
+            {
+                this.forall ((child) => {
+                    var row = child as StorageRow;
+                    if (row == null)
+                        return;
+
+                    row.set_selected(true);
+                    select_row((Gtk.ListBoxRow) row);
+                });
+            }
+        }
+
+        public void unselect_all_rows()
+        {
+            if(get_select_mode())
+            {
+                this.forall ((child) => {
+                    var row = child as StorageRow;
+                    if (row == null)
+                        return;
+
+                    row.set_selected(false);
+                    unselect_row((Gtk.ListBoxRow) row);
+                });
+            }
+        }
+
         private void load(string? path, StorageItemType? parent)
         {
             if(path == null)
@@ -140,10 +221,28 @@ namespace Usage
                     empty();
             });
         }
-
         private void on_row_activated (Gtk.ListBoxRow row)
         {
             StorageRow storage_row = (StorageRow) row;
+            if(get_select_mode())
+            {
+                if(storage_row.get_selected())
+                {
+                    storage_row.set_selected(false);
+                    unselect_row(row);
+                }
+                else
+                {
+                     storage_row.set_selected(true);
+                     select_row(row);
+                }
+            }
+            else
+                action_primary(storage_row);
+        }
+
+        private void action_primary (StorageRow storage_row)
+        {
             if(storage_row.get_item_type() != StorageItemType.STORAGE && storage_row.get_item_type() != 
StorageItemType.SYSTEM &&
                 storage_row.get_item_type() != StorageItemType.AVAILABLE && storage_row.get_item_type() != 
StorageItemType.FILE)
             {
@@ -157,15 +256,78 @@ namespace Usage
                 (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().set_title_text(actual_name);
                 (GLib.Application.get_default() as Application).get_window().get_header_bar().show_title();
+                if(actual_parent_type == StorageItemType.TRASHFILE || actual_parent_type == 
StorageItemType.TRASHSUBFILE)
+                    (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_select_button(false);
 
                 if(root)
                     color = storage_row.get_color();
 
                 load(actual_path, actual_parent_type);
             }
+            else
+                storage_row.action_primary();
+        }
+
+        protected override bool button_release_event (Gdk.EventButton event)
+        {
+            switch (event.button)
+            {
+            case Gdk.BUTTON_PRIMARY:
+
+                // Necessary to avoid treating an event from a child widget which would mess with getting 
the correct row.
+                if (event.window != this.get_window ())
+                     return false;
+
+                var row = this.get_row_at_y ((int) event.y);
+                if (row == null)
+                    return false;
+
+                StorageRow storage_row = (StorageRow) row;
+
+                if(get_select_mode())
+                {
+                    if(storage_row.get_selected())
+                    {
+                        storage_row.set_selected(false);
+                        unselect_row(row);
+                    }
+                    else
+                    {
+                         storage_row.set_selected(true);
+                         select_row(row);
+                    }
+                }
+                else
+                    action_primary(storage_row);
+
+                return true;
+            case Gdk.BUTTON_SECONDARY:
+                // Necessary to avoid treating an event from a child widget which would mess with getting 
the correct row.
+                if (event.window != this.get_window ())
+                     return false;
+
+                var row = this.get_row_at_y ((int) event.y);
+                if (row == null)
+                    return false;
+
+                StorageRow storage_row = (StorageRow) row;
+                storage_row.action_secondary();
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private void on_selected_rows_changed()
+        {
+            (GLib.Application.get_default() as 
Application).get_window().get_header_bar().change_selected_items(get_selected_rows().length());
+            if(get_selected_rows().length() > 0)
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_action_bar().set_sensitive_all(true);
+            else
+                ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_action_bar().set_sensitive_all(false);
         }
 
-        private Gtk.Widget on_row_created (Object item)
+        private Gtk.Widget on_row_created(Object item)
         {
             unowned StorageItem storage_item = (StorageItem) item;
             var row = new StorageRow(storage_item);
diff --git a/src/storage-row.vala b/src/storage-row.vala
index 9e97770..112cadd 100644
--- a/src/storage-row.vala
+++ b/src/storage-row.vala
@@ -7,6 +7,7 @@ namespace Usage
         private string item_path;
         private string item_name;
         private Gdk.RGBA color;
+        private Gtk.CheckButton? check_button;
 
                const GLib.ActionEntry[] action_entries = {
            { "rename", action_rename },
@@ -33,27 +34,28 @@ namespace Usage
             var title_label = new Gtk.Label(storage_item.get_name());
             title_label.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
 
+            Gtk.Widget? icon = null;
             switch(storage_item.get_item_type())
             {
                 case StorageItemType.SYSTEM:
-                    var color_rectangle = new ColorRectangle.new_from_css("system");
-                    color = color_rectangle.get_color();
-                    box.pack_start(color_rectangle, false, false, 5);
+                    icon = new ColorRectangle.new_from_css("system");
+                    color = ((ColorRectangle) icon).get_color();
+                    selectable = false;
                     break;
                 case StorageItemType.TRASH:
-                    var color_rectangle = new ColorRectangle.new_from_css("trash");
-                    color = color_rectangle.get_color();
-                    box.pack_start(color_rectangle, false, false, 5);
+                    check_button = new Gtk.CheckButton();
+                    icon = new ColorRectangle.new_from_css("trash");
+                    color = ((ColorRectangle) icon).get_color();
                     break;
                 case StorageItemType.USER:
-                    var color_rectangle = new ColorRectangle.new_from_css("user");
-                    color = color_rectangle.get_color();
-                    box.pack_start(color_rectangle, false, false, 5);
+                    check_button = new Gtk.CheckButton();
+                    icon = new ColorRectangle.new_from_css("user");
+                    color = ((ColorRectangle) icon).get_color();
                     break;
                 case StorageItemType.AVAILABLE:
-                    var color_rectangle = new ColorRectangle.new_from_css("available-storage");
-                    color = color_rectangle.get_color();
-                    box.pack_start(color_rectangle, false, false, 5);
+                    icon = new ColorRectangle.new_from_css("available-storage");
+                    color = ((ColorRectangle) icon).get_color();
+                    selectable = false;
                     break;
                 case StorageItemType.STORAGE:
                     title_label.set_markup ("<b>" + storage_item.get_name() + "</b>");
@@ -67,33 +69,33 @@ namespace Usage
                 case StorageItemType.MUSIC:
                 case StorageItemType.PICTURES:
                 case StorageItemType.VIDEOS:
+                    check_button = new Gtk.CheckButton();
                     get_style_context().add_class("folders");
                     color = get_style_context().get_color(get_style_context().get_state());
                     get_style_context().remove_class("folders");
-                    var color_rectangle = new ColorRectangle.new_from_rgba(storage_item.get_color());
-                    box.pack_start(color_rectangle, false, false, 5);
+                    icon = new ColorRectangle.new_from_rgba(storage_item.get_color());
                     break;
                 case StorageItemType.DIRECTORY:
+                    check_button = new Gtk.CheckButton();
                     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);
+                        icon = new Gtk.Image.from_pixbuf (pixbuf);
                     }
                     catch(Error e) {
                         GLib.stderr.printf ("Could not load folder-symbolic icon: %s\n", e.message);
                     }
                     break;
                  case StorageItemType.FILE:
+                    check_button = new Gtk.CheckButton();
                     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);
+                        icon = new Gtk.Image.from_pixbuf (pixbuf);
                     }
                     catch(Error e) {
                         GLib.stderr.printf ("Could not load folder-documents-symbolic icon: %s\n", 
e.message);
@@ -101,14 +103,58 @@ namespace Usage
                     break;
             }
 
+            if(check_button != null)
+            {
+                check_button.toggled.connect(() => {
+                    if(check_button.get_active())
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().select_row(this);
+                    else
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().unselect_row(this);
+                });
+                box.pack_start(check_button, false, false, 5);
+            }
+
+            if(icon != null)
+                box.pack_start(icon, false, false, 5);
+
             box.pack_start(title_label, false, true, 5);
             box.pack_end(size_label, false, true, 10);
-            var event_box = new Gtk.EventBox();
-            event_box.add(box);
-            event_box.button_press_event.connect(row_press_event);
-            add(event_box);
+            add(box);
 
             show_all();
+            set_show_check_button(false);
+        }
+
+        public void set_show_check_button(bool show)
+        {
+            if(check_button != null)
+            {
+                check_button.set_visible(show);
+                if(show == false)
+                    check_button.set_active(false);
+            }
+        }
+
+        public bool get_show_check_button()
+        {
+            if(check_button != null)
+                return check_button.get_visible();
+            else
+                return false;
+        }
+
+        public void set_selected(bool selected)
+        {
+            if(check_button != null)
+                check_button.set_active(selected);
+        }
+
+        public bool get_selected()
+        {
+             if(check_button == null)
+                return false;
+             else
+                return check_button.get_active();
         }
 
         public Gdk.RGBA get_color()
@@ -136,25 +182,7 @@ namespace Usage
             return parent_type;
         }
 
-        private bool row_press_event (Gdk.EventButton event)
-        {
-            if(event.type == Gdk.EventType.BUTTON_PRESS)
-            {
-                switch (event.button)
-                {
-                    case Gdk.BUTTON_PRIMARY:
-                        action_primary();
-                        return false;
-                    case Gdk.BUTTON_SECONDARY:
-                        action_secondary();
-                        return true;
-                }
-            }
-
-            return true;
-        }
-
-        private void action_primary()
+        public void action_primary()
         {
             if(type == StorageItemType.FILE)
             {
@@ -172,7 +200,7 @@ namespace Usage
             }
         }
 
-        private void action_secondary()
+        public void action_secondary()
         {
             bool show_popover = false;
 
@@ -285,7 +313,7 @@ namespace Usage
                     return false;
                 });
             }
-            dialog.close();
+            dialog.destroy();
         }
 
         private void action_wipe_trash()
@@ -305,7 +333,7 @@ namespace Usage
                     return false;
                 });
             }
-            dialog.close();
+            dialog.destroy();
         }
 
         private void action_rename()
@@ -370,7 +398,7 @@ namespace Usage
                     return false;
                 });
             }
-            chooser.close ();
+            chooser.destroy ();
         }
 
         private void action_move_to_trash()
@@ -387,14 +415,22 @@ namespace Usage
 
         private void action_delete()
         {
-            Timeout.add(0, () => {
-                var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
-                storage_analyzer.delete_file.begin(item_path, () => {
-                    ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
-                });
+            var dialog = new Gtk.MessageDialog ((GLib.Application.get_default() as 
Application).get_window(), Gtk.DialogFlags.MODAL,
+                Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("Are you sure you want to permanently 
delete %s?").printf(item_name));
+            dialog.secondary_text = _("If you delete an item, it will be permanently lost.");
 
-                return false;
-            });
+            if(dialog.run() == Gtk.ResponseType.OK)
+            {
+               Timeout.add(0, () => {
+                    var storage_analyzer = (GLib.Application.get_default() as 
Application).get_storage_analyzer();
+                    storage_analyzer.delete_file.begin(item_path, () => {
+                        ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().refresh();
+                    });
+
+                    return false;
+                });
+            }
+            dialog.destroy();
         }
     }
 }
diff --git a/src/storage-view.vala b/src/storage-view.vala
index 4e984c5..e13d15d 100644
--- a/src/storage-view.vala
+++ b/src/storage-view.vala
@@ -3,6 +3,8 @@ namespace Usage
     public class StorageView : View
     {
         private StorageListBox storage_list_box;
+        private Gtk.Revealer revealer;
+        private StorageActionBar action_bar;
 
         public StorageView ()
         {
@@ -64,12 +66,33 @@ namespace Usage
                 empty_label.show();
             });
 
-            add(paned);
+            action_bar = new StorageActionBar();
+
+            revealer = new Gtk.Revealer();
+            revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_UP;
+            revealer.transition_duration = 400;
+            revealer.add(action_bar);
+
+            var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+            box.pack_start(paned, true);
+            box.pack_end(revealer, false);
+            add(box);
         }
 
         public StorageListBox get_storage_list_box()
         {
             return storage_list_box;
         }
+
+        public StorageActionBar get_action_bar()
+        {
+            return action_bar;
+        }
+
+        public void show_action_bar(bool show)
+        {
+            revealer.set_reveal_child(show);
+            action_bar.hide_all();
+        }
     }
 }



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