summaryrefslogtreecommitdiff
path: root/src/folders
diff options
context:
space:
mode:
Diffstat (limited to 'src/folders')
-rw-r--r--src/folders/Branch.vala198
-rw-r--r--src/folders/Folders.vala35
-rw-r--r--src/folders/Page.vala41
-rw-r--r--src/folders/mk/folders.mk31
4 files changed, 305 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);
+ }
+}
diff --git a/src/folders/Folders.vala b/src/folders/Folders.vala
new file mode 100644
index 0000000..1cc14b1
--- /dev/null
+++ b/src/folders/Folders.vala
@@ -0,0 +1,35 @@
+/* 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.
+ */
+
+/* This file is the master unit file for the Folders unit. It should be edited to include
+ * whatever code is deemed necessary.
+ *
+ * The init() and terminate() methods are mandatory.
+ *
+ * If the unit needs to be configured prior to initialization, add the proper parameters to
+ * the preconfigure() method, implement it, and ensure in init() that it's been called.
+ */
+
+namespace Folders {
+
+static Icon? opened_icon = null;
+static Icon? closed_icon = null;
+static Icon? have_photos_icon = null;
+
+public void init() throws Error {
+ opened_icon = new ThemedIcon(Resources.ICON_FOLDER_OPEN);
+ closed_icon = new ThemedIcon(Resources.ICON_FOLDER_CLOSED);
+ have_photos_icon = new ThemedIcon(Resources.ICON_FOLDER_DOCUMENTS);
+}
+
+public void terminate() {
+ opened_icon = null;
+ closed_icon = null;
+ have_photos_icon = null;
+}
+
+}
+
diff --git a/src/folders/Page.vala b/src/folders/Page.vala
new file mode 100644
index 0000000..d101e88
--- /dev/null
+++ b/src/folders/Page.vala
@@ -0,0 +1,41 @@
+/* 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.Page : CollectionPage {
+ private class FolderViewManager : CollectionViewManager {
+ public File dir;
+
+ public FolderViewManager(Folders.Page owner, File dir) {
+ base (owner);
+
+ this.dir = dir;
+ }
+
+ public override bool include_in_view(DataSource source) {
+ return ((MediaSource) source).get_file().has_prefix(dir);
+ }
+ }
+
+ private FolderViewManager view_manager;
+
+ public Page(File dir) {
+ base (dir.get_path());
+
+ view_manager = new FolderViewManager(this, dir);
+
+ foreach (MediaSourceCollection sources in MediaCollectionRegistry.get_instance().get_all())
+ get_view().monitor_source_collection(sources, view_manager, null);
+ }
+
+ protected override void get_config_photos_sort(out bool sort_order, out int sort_by) {
+ Config.Facade.get_instance().get_library_photos_sort(out sort_order, out sort_by);
+ }
+
+ protected override void set_config_photos_sort(bool sort_order, int sort_by) {
+ Config.Facade.get_instance().set_library_photos_sort(sort_order, sort_by);
+ }
+}
+
diff --git a/src/folders/mk/folders.mk b/src/folders/mk/folders.mk
new file mode 100644
index 0000000..d0023d7
--- /dev/null
+++ b/src/folders/mk/folders.mk
@@ -0,0 +1,31 @@
+
+# UNIT_NAME is the Vala namespace. A file named UNIT_NAME.vala must be in this directory with
+# a init() and terminate() function declared in the namespace.
+UNIT_NAME := Folders
+
+# UNIT_DIR should match the subdirectory the files are located in. Generally UNIT_NAME in all
+# lowercase. The name of this file should be UNIT_DIR.mk.
+UNIT_DIR := folders
+
+# All Vala files in the unit should be listed here with no subdirectory prefix.
+#
+# NOTE: Do *not* include the unit's master file, i.e. UNIT_NAME.vala.
+UNIT_FILES := \
+ Branch.vala \
+ Page.vala
+
+# Any unit this unit relies upon (and should be initialized before it's initialized) should
+# be listed here using its Vala namespace.
+#
+# NOTE: All units are assumed to rely upon the unit-unit. Do not include that here.
+UNIT_USES := \
+ Sidebar \
+ Photos
+
+# List any additional files that are used in the build process as a part of this unit that should
+# be packaged in the tarball. File names should be relative to the unit's home directory.
+UNIT_RC :=
+
+# unitize.mk must be called at the end of each UNIT_DIR.mk file.
+include unitize.mk
+