summaryrefslogtreecommitdiff
path: root/src/folders/Branch.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/folders/Branch.vala')
-rw-r--r--src/folders/Branch.vala198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/folders/Branch.vala b/src/folders/Branch.vala
new file mode 100644
index 0000000..bc5b578
--- /dev/null
+++ b/src/folders/Branch.vala
@@ -0,0 +1,198 @@
+/* Copyright 2012-2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class Folders.Branch : Sidebar.Branch {
+ private Gee.HashMap<File, Folders.SidebarEntry> entries =
+ new Gee.HashMap<File, Folders.SidebarEntry>(file_hash, file_equal);
+ private File home_dir;
+
+ public class Branch() {
+ base (new Folders.Root(), Sidebar.Branch.Options.STARTUP_OPEN_GROUPING, comparator);
+
+ home_dir = File.new_for_path(Environment.get_home_dir());
+
+ foreach (MediaSourceCollection sources in MediaCollectionRegistry.get_instance().get_all()) {
+ // seed
+ on_media_contents_altered(sources.get_all(), null);
+
+ // monitor
+ sources.contents_altered.connect(on_media_contents_altered);
+ }
+ }
+
+ ~Branch() {
+ foreach (MediaSourceCollection sources in MediaCollectionRegistry.get_instance().get_all())
+ sources.contents_altered.disconnect(on_media_contents_altered);
+ }
+
+ private static int comparator(Sidebar.Entry a, Sidebar.Entry b) {
+ if (a == b)
+ return 0;
+
+ int coll_key_equality = strcmp(((Folders.SidebarEntry) a).collation,
+ ((Folders.SidebarEntry) b).collation);
+
+ if (coll_key_equality == 0) {
+ // Collation keys were the same, double-check that
+ // these really are the same string...
+ return strcmp(((Folders.SidebarEntry) a).get_sidebar_name(),
+ ((Folders.SidebarEntry) b).get_sidebar_name());
+ }
+
+ return coll_key_equality;
+ }
+
+ private void on_master_source_replaced(MediaSource media_source, File old_file, File new_file) {
+ remove_entry(old_file);
+ add_entry(media_source);
+ }
+
+ private void on_media_contents_altered(Gee.Iterable<DataObject>? added, Gee.Iterable<DataObject>? removed) {
+ if (added != null) {
+ foreach (DataObject object in added) {
+ add_entry((MediaSource) object);
+ ((MediaSource) object).master_replaced.connect(on_master_source_replaced);
+ }
+ }
+
+ if (removed != null) {
+ foreach (DataObject object in removed) {
+ remove_entry(((MediaSource) object).get_file());
+ ((MediaSource) object).master_replaced.disconnect(on_master_source_replaced);
+ }
+ }
+ }
+
+ void add_entry(MediaSource media) {
+ File file = media.get_file();
+
+ Gee.ArrayList<File> elements = new Gee.ArrayList<File>();
+
+ // add the path elements in reverse order up to home directory
+ File? parent = file.get_parent();
+ while (parent != null) {
+ // don't process paths above the user's home directory
+ if (parent.equal(home_dir.get_parent()))
+ break;
+
+ elements.add(parent);
+
+ parent = parent.get_parent();
+ }
+
+ // walk path elements in order from home directory down, building needed sidebar entries
+ // along the way
+ Folders.SidebarEntry? parent_entry = null;
+ for (int ctr = elements.size - 1; ctr >= 0; ctr--) {
+ File parent_dir = elements[ctr];
+
+ // save current parent, needed if this entry needs to be grafted
+ Folders.SidebarEntry? old_parent_entry = parent_entry;
+
+ parent_entry = entries.get(parent_dir);
+ if (parent_entry == null) {
+ parent_entry = new Folders.SidebarEntry(parent_dir);
+ entries.set(parent_dir, parent_entry);
+
+ graft((old_parent_entry == null) ? get_root() : old_parent_entry, parent_entry);
+ }
+
+ // only increment entry's file count if File is going in this folder
+ if (ctr == 0)
+ parent_entry.count++;
+ }
+ }
+
+ private void remove_entry(File file) {
+ Folders.SidebarEntry? folder_entry = entries.get(file.get_parent());
+ if (folder_entry == null)
+ return;
+
+ assert(folder_entry.count > 0);
+
+ // decrement file count for folder of photo
+ if (--folder_entry.count > 0 || get_child_count(folder_entry) > 0)
+ return;
+
+ // empty folder so prune tree
+ Folders.SidebarEntry? prune_point = folder_entry;
+ assert(prune_point != null);
+
+ for (;;) {
+ bool removed = entries.unset(prune_point.dir);
+ assert(removed);
+
+ Folders.SidebarEntry? parent = get_parent(prune_point) as Folders.SidebarEntry;
+ if (parent == null || parent.count != 0 || get_child_count(parent) > 1)
+ break;
+
+ prune_point = parent;
+ }
+
+ prune(prune_point);
+ }
+}
+
+private class Folders.Root : Sidebar.Grouping {
+ public Root() {
+ base (_("Folders"), Folders.opened_icon, Folders.closed_icon);
+ }
+}
+
+public class Folders.SidebarEntry : Sidebar.SimplePageEntry, Sidebar.ExpandableEntry {
+ public File dir { get; private set; }
+ public string collation { get; private set; }
+
+ private int _count = 0;
+ public int count {
+ get {
+ return _count;
+ }
+
+ set {
+ int prev_count = _count;
+ _count = value;
+
+ // when count change 0->1 and 1->0 may need refresh icon
+ if ((prev_count == 0 && _count == 1) || (prev_count == 1 && _count == 0))
+ sidebar_icon_changed(get_sidebar_icon());
+
+ }
+ }
+
+ public SidebarEntry(File dir) {
+ this.dir = dir;
+ collation = g_utf8_collate_key_for_filename(dir.get_path());
+ }
+
+ public override string get_sidebar_name() {
+ return dir.get_basename();
+ }
+
+ public override Icon? get_sidebar_icon() {
+ return count == 0 ? closed_icon : have_photos_icon;
+ }
+
+ public override string to_string() {
+ return dir.get_path();
+ }
+
+ public Icon? get_sidebar_open_icon() {
+ return count == 0 ? opened_icon : have_photos_icon;
+ }
+
+ public Icon? get_sidebar_closed_icon() {
+ return count == 0 ? closed_icon : have_photos_icon;
+ }
+
+ public bool expand_on_select() {
+ return true;
+ }
+
+ protected override global::Page create_page() {
+ return new Folders.Page(dir);
+ }
+}