[f-spot: 1/3] new folderview sidepane



commit 29e51ffa2f4214ab782fe6f2266b5ef2b74598a1
Author: Mike Gemünde <mike gemuende de>
Date:   Tue Jun 23 23:04:07 2009 +0200

    new folderview sidepane
    
    bump db to v17, uri spliited in base_uri and filename, new FolderTreePage widget

 src/DirectoryAdaptor.cs       |  131 ----------
 src/DirectoryCollection.cs    |   52 ----
 src/FSpot.addin.xml           |    1 +
 src/FolderQueryWidget.cs      |  115 +++++++++
 src/GroupSelector.cs          |   11 -
 src/MainWindow.cs             |   70 +-----
 src/Makefile.am               |    5 +-
 src/PhotoQuery.cs             |    1 +
 src/PhotoStore.cs             |  139 +++++++----
 src/Preferences.cs            |    2 -
 src/Query/FolderSet.cs        |   54 ++++
 src/Query/LogicalTerm.cs      |    2 +-
 src/Query/OrderByTime.cs      |    2 +-
 src/QueryWidget.cs            |   43 +++-
 src/Term.cs                   |    2 +-
 src/Updater.cs                |   90 +++++++-
 src/Utils/UriUtils.cs         |    2 +-
 src/Widgets/FolderTreePage.cs |  539 +++++++++++++++++++++++++++++++++++++++++
 src/ui/main_window.ui         |   20 --
 19 files changed, 938 insertions(+), 343 deletions(-)
---
diff --git a/src/FSpot.addin.xml b/src/FSpot.addin.xml
index 0147759..3fd7420 100644
--- a/src/FSpot.addin.xml
+++ b/src/FSpot.addin.xml
@@ -60,6 +60,7 @@
 		<Condition id="ViewMode" mode="library">
 			<SidebarPage sidebar_page_type = "FSpot.Widgets.EditorPage" />
 		</Condition>
+		<SidebarPage sidebar_page_type = "FSpot.Widgets.FolderTreePage" />
 	</Extension>
 
 	<Extension path = "/FSpot/Editors">
diff --git a/src/FolderQueryWidget.cs b/src/FolderQueryWidget.cs
new file mode 100644
index 0000000..e150e9d
--- /dev/null
+++ b/src/FolderQueryWidget.cs
@@ -0,0 +1,115 @@
+/*
+ * FSpot.Gui.FolderQueryWidget.cs
+ *
+ * Author(s)
+ * 	Mike Gemuende <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+
+using System;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+using Gtk;
+
+using FSpot;
+using FSpot.Utils;
+using FSpot.Query;
+
+namespace FSpot.Gui
+{
+	
+	
+	public class FolderQueryWidget : HBox
+	{
+		PhotoQuery query;
+		FolderSet folder_set;
+		
+		public FolderQueryWidget ()
+		{
+			folder_set = new FolderSet ();
+			query = MainWindow.Toplevel.Query;
+			
+			query.SetCondition (folder_set);
+			
+			Drag.DestSet (this, DestDefaults.All,
+			              folder_query_widget_source_table,
+			              Gdk.DragAction.Copy | Gdk.DragAction.Move);
+		}
+		
+		private void UpdateGui ()
+		{
+			while (Children.Length != 0)
+				Remove (Children[0]);
+			
+			int length = folder_set.Folders.Count ();
+			
+			if (length == 0) {
+				Hide ();
+				return;
+			}
+			
+			if (length < 4) {
+				
+				foreach (Uri uri in folder_set.Folders) {
+					Image image = new Image ("gtk-directory", IconSize.Button);
+					image.TooltipText = uri.ToString ();
+					PackStart (image);
+				}
+				
+				TooltipText = String.Empty;
+				
+			} else {
+				
+				Label label = new Label (String.Format ("<i>{0}x</i>", length));
+				label.UseMarkup = true;
+				PackStart (label);
+				
+				Image image = new Image ("gtk-directory", IconSize.Button);
+				PackStart (image);
+				
+				StringBuilder builder = new StringBuilder ();
+				foreach (Uri uri in folder_set.Folders) {
+					if (builder.Length > 0)
+						builder.AppendLine ();
+					
+					builder.Append (uri.ToString ());
+				}
+				
+				TooltipText = builder.ToString ();
+			}
+			
+			ShowAll ();
+		}
+		
+		public void SetFolders (IEnumerable<Uri> uris)
+		{
+			folder_set.Folders = uris;
+			
+			UpdateGui ();
+		}
+		
+		public void Clear ()
+		{
+			folder_set.Folders = null;
+		}
+		
+		private static TargetEntry [] folder_query_widget_source_table =
+			new TargetEntry [] {
+				DragDropTargets.UriQueryEntry
+		};
+		
+		protected override void OnDragDataReceived (Gdk.DragContext context, int x, int y, Gtk.SelectionData selection_data, uint info, uint time_)
+		{
+			base.OnDragDataReceived (context, x, y, selection_data, info, time_);
+			
+			SetFolders (selection_data.GetUriListData ());
+			query.RequestReload ();
+		}
+
+	}
+}
diff --git a/src/GroupSelector.cs b/src/GroupSelector.cs
index e34ae5f..0ad845b 100644
--- a/src/GroupSelector.cs
+++ b/src/GroupSelector.cs
@@ -56,9 +56,6 @@ namespace FSpot {
 				if (adaptor is TimeAdaptor) {
 					MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More dates"), null);
 					MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More dates"), null);
-				} else if (adaptor is DirectoryAdaptor) {
-					MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More directories"), null);
-					MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More directories"), null);
 				} else {
 					MainWindow.ToolTips.SetTip (left, Catalog.GetString ("More"), null);
 					MainWindow.ToolTips.SetTip (right, Catalog.GetString ("More"), null);
@@ -512,14 +509,6 @@ namespace FSpot {
 		{
 			Gtk.Menu order_menu = new Gtk.Menu();
 			
-			GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("Arrange by _Month"),
-					      MainWindow.Toplevel.HandleArrangeByTime, true, (adaptor is TimeAdaptor), true);
-			
-			GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("Arrange by _Folder"),
-					      MainWindow.Toplevel.HandleArrangeByDirectory, true, (adaptor is DirectoryAdaptor), true);
-
-			GtkUtil.MakeMenuSeparator (order_menu);
-
 			GtkUtil.MakeCheckMenuItem (order_menu, Catalog.GetString ("_Reverse Order"),
 					      MainWindow.Toplevel.HandleReverseOrder, true, adaptor.OrderAscending, false);
 
diff --git a/src/MainWindow.cs b/src/MainWindow.cs
index 5723385..8ccb208 100644
--- a/src/MainWindow.cs
+++ b/src/MainWindow.cs
@@ -83,8 +83,6 @@ public class MainWindow {
 	[GtkBeans.Builder.Object] Gtk.Action remove_tag;
 
 	// View
-	[GtkBeans.Builder.Object] Gtk.RadioAction month;
-	[GtkBeans.Builder.Object] Gtk.RadioAction directory;
 	[GtkBeans.Builder.Object] Gtk.ToggleAction display_toolbar;
 	[GtkBeans.Builder.Object] Gtk.ToggleAction display_sidebar;
 	[GtkBeans.Builder.Object] Gtk.ToggleAction display_timeline;
@@ -477,7 +475,6 @@ public class MainWindow {
 		view_notebook.SwitchPage += HandleViewNotebookSwitchPage;
 		group_selector.Adaptor.GlassSet += HandleAdaptorGlassSet;
 		group_selector.Adaptor.Changed += HandleAdaptorChanged;
-		LoadPreference (Preferences.GROUP_ADAPTOR);
 		LoadPreference (Preferences.GROUP_ADAPTOR_ORDER_ASC);
 
 		this.selection = new MainSelection (this);
@@ -933,6 +930,12 @@ public class MainWindow {
 		}
 	}
 	
+	public void SetFolderQuery (IEnumerable<Uri> uri_list)
+	{
+		ShowQueryWidget ();
+		query_widget.SetFolders (uri_list);
+	}
+	
 	public void AddTagsQuery (Tag [] tags)
 	{
 		ShowQueryWidget ();
@@ -969,22 +972,14 @@ public class MainWindow {
 		FSpot.SimpleCalendar cal = sender as FSpot.SimpleCalendar;
 		JumpTo (cal.Date);
 	}
-#endif
 
-	private void JumpTo (System.DateTime time)
+	void JumpTo (System.DateTime time)
 	{
-		//FIXME this should make sure the photos are sorted by
-		//time.  This should be handled via a property that
-		//does all the needed switching.
-		if (!(group_selector.Adaptor is FSpot.TimeAdaptor))
-			HandleArrangeByTime (null, null);
-		
-		FSpot.TimeAdaptor time_adaptor = group_selector.Adaptor as FSpot.TimeAdaptor;
-		if (time_adaptor != null)
-			JumpTo (query.LookupItem (time));
+		JumpTo (query.LookupItem (time));*/
 	}
+#endif
 
-	private void JumpTo (int index)
+	void JumpTo (int index)
 	{
 		switch (view_mode) {
 		case ModeType.PhotoView:
@@ -1618,44 +1613,6 @@ public class MainWindow {
 			Preferences.Set (Preferences.TAG_ICON_SIZE, TagsIconSize);
 		}
 	}
-
-	public void HandleArrangeByTime (object sender, EventArgs args)
-	{
-		if (group_selector.Adaptor is TimeAdaptor)
-			return;
-
-		group_selector.Adaptor.GlassSet -= HandleAdaptorGlassSet;
-		group_selector.Adaptor.Changed -= HandleAdaptorChanged;
-		group_selector.Adaptor = new FSpot.TimeAdaptor (query, reverse_order.Active);
-
-		group_selector.Mode = FSpot.GroupSelector.RangeType.Min;
-		group_selector.Adaptor.GlassSet += HandleAdaptorGlassSet;
-		group_selector.Adaptor.Changed += HandleAdaptorChanged;
-
-		if (sender != month)
-			month.Active = true;
-
-		//update the selection in the Timeline
-		if (query.Range != null)
-			group_selector.SetLimitsToDates(query.Range.Start, query.Range.End);
-	}
-
-	public void HandleArrangeByDirectory (object sender, EventArgs args)
-	{
-		if (group_selector.Adaptor is DirectoryAdaptor)
-			return;
-
-		group_selector.Adaptor.GlassSet -= HandleAdaptorGlassSet;
-		group_selector.Adaptor.Changed -= HandleAdaptorChanged;
-		group_selector.Adaptor = new FSpot.DirectoryAdaptor (query, reverse_order.Active); 	
-
-		group_selector.Mode = FSpot.GroupSelector.RangeType.Min;
-		group_selector.Adaptor.GlassSet += HandleAdaptorGlassSet;
-		group_selector.Adaptor.Changed += HandleAdaptorChanged;
-
-		if (sender != directory)
-			directory.Active = true;
-	}
 	
 	public void HandleReverseOrder (object sender, EventArgs args)
 	{
@@ -1715,8 +1672,6 @@ public class MainWindow {
 		Preferences.Set (Preferences.SHOW_DATES,		icon_view.DisplayDates);
 		Preferences.Set (Preferences.SHOW_RATINGS,		icon_view.DisplayRatings);
 
-		Preferences.Set (Preferences.GROUP_ADAPTOR,		(group_selector.Adaptor is DirectoryAdaptor) ? 1 : 0);
-		Preferences.Set (Preferences.GROUP_ADAPTOR_ORDER_ASC,   group_selector.Adaptor.OrderAscending);
 		Preferences.Set (Preferences.GLASS_POSITION,		group_selector.GlassPosition);
 		
 		Preferences.Set (Preferences.SIDEBAR_POSITION,		main_hpaned.Position);
@@ -2536,11 +2491,6 @@ public class MainWindow {
 			if (display_ratings_menu_item.Active != Preferences.Get<bool> (key))
 				display_ratings_menu_item.Active = Preferences.Get<bool> (key);
 			break;
-		
-		case Preferences.GROUP_ADAPTOR:
-			if (Preferences.Get<int> (key) == 1)
-				directory.Active = true;
-			break;
 
 		case Preferences.GROUP_ADAPTOR_ORDER_ASC:
 			group_selector.Adaptor.OrderAscending = Preferences.Get<bool> (key);
diff --git a/src/Makefile.am b/src/Makefile.am
index bf79e46..e31b793 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,7 @@ CORE_CSDISTFILES =				\
 
 QUERY_CSDISTFILES =				\
 	$(srcdir)/Query/DateRange.cs		\
+	$(srcdir)/Query/FolderSet.cs		\
 	$(srcdir)/Query/IOrderCondition.cs	\
 	$(srcdir)/Query/IQueryCondition.cs	\
 	$(srcdir)/Query/LogicalTerm.cs		\
@@ -129,8 +130,6 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/Core/PhotoVersion.cs		\
 	$(srcdir)/Db.cs				\
 	$(srcdir)/DependentListStore.cs		\
-	$(srcdir)/DirectoryAdaptor.cs		\
-	$(srcdir)/DirectoryCollection.cs	\
 	$(srcdir)/DragDropTargets.cs				\
 	$(srcdir)/Editors/Editor.cs		\
 	$(srcdir)/Editors/AutoStretchEditor.cs		\
@@ -168,6 +167,7 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/Filters/SharpFilter.cs	\
 	$(srcdir)/Filters/UniqueNameFilter.cs	\
 	$(srcdir)/Filters/WhiteListFilter.cs	\
+	$(srcdir)/FolderQueryWidget.cs			\
 	$(srcdir)/FormClient.cs			\
 	$(srcdir)/FullScreenView.cs		\
 	$(srcdir)/GdkGlx.cs			\
@@ -279,6 +279,7 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/Widgets/EditorPage.cs		\
 	$(srcdir)/Widgets/Filmstrip.cs		\
 	$(srcdir)/Widgets/FindBar.cs		\
+	$(srcdir)/Widgets/FolderTreePage.cs	\
 	$(srcdir)/Widgets/IEffect.cs		\
 	$(srcdir)/Widgets/ITransition.cs	\
 	$(srcdir)/Widgets/IconView.cs		\
diff --git a/src/PhotoQuery.cs b/src/PhotoQuery.cs
index 7d84aa8..1f95ae2 100644
--- a/src/PhotoQuery.cs
+++ b/src/PhotoQuery.cs
@@ -274,6 +274,7 @@ namespace FSpot {
 
 			if (Changed != null)
 				Changed (this);
+			
 			Log.DebugTimerPrint (timer, "Reloading the query took {0}");
 		}
 		
diff --git a/src/PhotoStore.cs b/src/PhotoStore.cs
index cd0ba39..688d171 100644
--- a/src/PhotoStore.cs
+++ b/src/PhotoStore.cs
@@ -71,7 +71,7 @@ public class PhotoStore : DbStore<Photo> {
 			try {
 				thumbnail = ((FSpot.IThumbnailContainer)img).GetEmbeddedThumbnail ();
 			} catch (Exception e) {
-				Log.Debug ("Exception while loading embedded thumbail {0}", e.ToString ());
+				Log.DebugFormat ("Exception while loading embedded thumbail {0}", e.ToString ());
 			}
 		}
 
@@ -85,13 +85,6 @@ public class PhotoStore : DbStore<Photo> {
 		return thumbnail;
 	}
 
-
-//	public static void MoveThumbnail (string old_path, string new_path)
-//	{
-//		System.IO.File.Move (ThumbnailGenerator.ThumbnailPath (UriUtils.PathToFileUri (old_path)),
-//			   ThumbnailGenerator.ThumbnailPath(UriUtils.PathToFileUri (new_path)));
-//	}
-
 	// Constructor
 
 	public PhotoStore (QueuedSqliteDatabase database, bool is_new)
@@ -106,7 +99,8 @@ public class PhotoStore : DbStore<Photo> {
 			"CREATE TABLE photos (\n" +
 			"	id			INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \n" +
 			"	time			INTEGER NOT NULL, \n" +
-			"	uri			STRING NOT NULL, \n" +
+			"	base_uri		STRING NOT NULL, \n" +
+		    "	filename		STRING NOT NULL, \n" +
 			"	description		TEXT NOT NULL, \n" +
 			"	roll_id			INTEGER NOT NULL, \n" +
 			"	default_version_id	INTEGER NOT NULL, \n" +
@@ -126,7 +120,8 @@ public class PhotoStore : DbStore<Photo> {
 			"	photo_id	INTEGER, \n" +
 			"	version_id	INTEGER, \n" +
 			"	name		STRING, \n" +
-			"	uri		STRING NOT NULL, \n" +
+			"	base_uri		STRING NOT NULL, \n" +
+		    "	filename		STRING NOT NULL, \n" +
 			"	md5_sum		TEXT NULL, \n" +
 			"	protected	BOOLEAN, \n" +
 			"	UNIQUE (photo_id, version_id)\n" +
@@ -194,10 +189,11 @@ public class PhotoStore : DbStore<Photo> {
 
 	 		uint id = (uint) Database.Execute (
 				new DbCommand (
-					"INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating, md5_sum) "	+
-	 				"VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating, :md5_sum)",
+					"INSERT INTO photos (time, base_uri, filename, description, roll_id, default_version_id, rating, md5_sum) "	+
+					"VALUES (:time, :base_uri, :filename, :description, :roll_id, :default_version_id, :rating, :md5_sum)",
 	 				"time", unix_time,
-					"uri", new_uri.OriginalString,
+					"base_uri", new_uri.GetDirectoryUri ().ToString (),
+					"filename", new_uri.GetFilename (),
 	 				"description", description,
 					"roll_id", roll_id,
 	 				"default_version_id", Photo.OriginalVersionId,
@@ -220,7 +216,7 @@ public class PhotoStore : DbStore<Photo> {
 	private void GetVersions (Photo photo)
 	{
 		SqliteDataReader reader = Database.Query(
-			new DbCommand("SELECT version_id, name, uri, md5_sum, protected " + 
+			new DbCommand("SELECT version_id, name, base_uri, filename, md5_sum, protected " + 
 				      "FROM photo_versions " + 
 				      "WHERE photo_id = :id", 
 				      "id", photo.Id
@@ -230,9 +226,10 @@ public class PhotoStore : DbStore<Photo> {
 		while (reader.Read ()) {
 			uint version_id = Convert.ToUInt32 (reader ["version_id"]);
 			string name = reader["name"].ToString ();
-			System.Uri uri = new System.Uri (reader["uri"].ToString ());
+			System.Uri uri = new Uri (new Uri (reader ["base_uri"].ToString ()), reader ["filename"].ToString ());
 			string md5_sum = reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null;
 			bool is_protected = Convert.ToBoolean (reader["protected"]);
+			                              
 			photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
 		}
 		reader.Close();
@@ -251,7 +248,7 @@ public class PhotoStore : DbStore<Photo> {
 	}		
 	
 	private void GetAllVersions  () {
-		SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, uri, md5_sum, protected FROM photo_versions");
+		SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, base_uri, filename, md5_sum, protected FROM photo_versions");
 		
 		while (reader.Read ()) {
 			uint id = Convert.ToUInt32 (reader ["photo_id"]);
@@ -270,9 +267,10 @@ public class PhotoStore : DbStore<Photo> {
 			if (reader ["version_id"] != null) {
 				uint version_id = Convert.ToUInt32 (reader ["version_id"]);
 				string name = reader["name"].ToString ();
-				System.Uri uri = new System.Uri (reader["uri"].ToString ());
+				System.Uri uri = new Uri (new Uri (reader ["base_uri"].ToString ()), reader ["filename"].ToString ());
 				string md5_sum = reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null;
 				bool is_protected = Convert.ToBoolean (reader["protected"]);
+				
 				photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
 			}
 
@@ -317,9 +315,9 @@ public class PhotoStore : DbStore<Photo> {
 		Photo photo = LookupInCache (id);
 		if (photo != null)
 			return photo;
-
+		
 		SqliteDataReader reader = Database.Query(
-			new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating, md5_sum " + 
+			new DbCommand("SELECT time, base_uri, filename, description, roll_id, default_version_id, rating, md5_sum " + 
 				      "FROM photos " + 
 				      "WHERE id = :id", "id", id
 				     )
@@ -328,7 +326,7 @@ public class PhotoStore : DbStore<Photo> {
 		if (reader.Read ()) {
 			photo = new Photo (id,
 				Convert.ToInt64 (reader ["time"]),
-				new System.Uri (reader ["uri"].ToString ()),
+			    new Uri (new Uri (reader ["base_uri"].ToString ()), reader ["filename"].ToString ()),
 				reader["md5_sum"] != null ? reader["md5_sum"].ToString () : null
 			);
 
@@ -353,7 +351,7 @@ public class PhotoStore : DbStore<Photo> {
 	public Photo GetByPath (string path)
 	{
 		return GetByUri (UriUtils.PathToFileUri (path));
-	}
+	}	
 
 	public Photo GetByUri (System.Uri uri)
 	{
@@ -361,10 +359,14 @@ public class PhotoStore : DbStore<Photo> {
 
 		uint timer = Log.DebugTimerStart ();
 
-		SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating, photos.md5_sum AS md5_sum " +
-									 " FROM photos " +
-									 " LEFT JOIN photo_versions AS pv ON photos.id = pv.photo_id" +
-							                 " WHERE photos.uri = :uri OR pv.uri = :uri", "uri", uri.ToString ()));
+		SqliteDataReader reader =
+			Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating, photos.md5_sum AS md5_sum " +
+			                               " FROM photos " +
+			                               " LEFT JOIN photo_versions AS pv ON photos.id = pv.photo_id" +
+			                               " WHERE (photos.base_uri = :base_uri AND photos.filename = :filename)" +
+			                               " OR (pv.base_uri = :base_uri AND pv.filename = :filename)",
+			                               "base_uri", uri.GetDirectoryUri ().ToString (),
+			                               "filename", uri.GetFilename ()));
 
 		if (reader.Read ()) {
 			photo = new Photo (Convert.ToUInt32 (reader ["id"]),
@@ -377,7 +379,8 @@ public class PhotoStore : DbStore<Photo> {
 			photo.DefaultVersionId = Convert.ToUInt32 (reader["default_version_id"]);
 			photo.Rating = Convert.ToUInt32 (reader ["rating"]);
 		}
-	        reader.Close();
+		
+		reader.Close();
 		Log.DebugTimerPrint (timer, "GetByUri query took {0}");
 
 		if (photo == null)
@@ -402,7 +405,7 @@ public class PhotoStore : DbStore<Photo> {
 		
 		SqliteDataReader reader = Database.Query (
 			new DbCommand ("SELECT DISTINCT " + 
-				       "id, time, photos.uri AS uri, description, roll_id, default_version_id, rating " + 
+				       "id, time, photos.base_uri AS uri, photos.filename AS filename, description, roll_id, default_version_id, rating " + 
 				       "FROM photos " + 
 				       "LEFT JOIN photo_versions " + 
 				       "ON   photos.id = photo_versions.photo_id " +
@@ -413,11 +416,11 @@ public class PhotoStore : DbStore<Photo> {
 		);
 
 		while (reader.Read ()) {
-			Photo photo = new Photo (Convert.ToUInt32 (reader ["id"]),
-				Convert.ToInt64 (reader ["time"]),
-				new System.Uri (reader ["uri"].ToString ()),
-				md5_sum
-			);
+			Photo photo =
+				new Photo (Convert.ToUInt32 (reader ["id"]),
+				           Convert.ToInt64 (reader ["time"]),
+				           new Uri (new Uri (reader ["base_uri"].ToString ()), reader ["filename"].ToString ()),
+				           md5_sum);
 
 			photo.Description = reader["description"].ToString ();
 			photo.RollId = Convert.ToUInt32 (reader["roll_id"]);
@@ -519,14 +522,16 @@ public class PhotoStore : DbStore<Photo> {
 					"SET description = :description, " + 
 					"    default_version_id = :default_version_id, " + 
 					"    time = :time, " + 
-					"    uri = :uri, " +
+					"    base_uri = :base_uri, " +
+					"    filename = :filename, " +       
 					"    rating = :rating, " +
 					"    md5_sum = :md5_sum	" +
 					"WHERE id = :id ",
 					"description", photo.Description,
 					"default_version_id", photo.DefaultVersionId,
 					"time", DbUtils.UnixTimeFromDateTime (photo.Time),
-					"uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
+					"base_uri", photo.VersionUri (Photo.OriginalVersionId).GetDirectoryUri ().ToString (),
+					"filename", photo.VersionUri (Photo.OriginalVersionId).GetFilename (),
 					"rating", String.Format ("{0}", photo.Rating),
 					"md5_sum", (photo.MD5Sum != String.Empty ? photo.MD5Sum : null),
 					"id", photo.Id
@@ -561,12 +566,13 @@ public class PhotoStore : DbStore<Photo> {
 			foreach (uint version_id in changes.VersionsAdded) {
 				PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
 				Database.ExecuteNonQuery (new DbCommand (
-					"INSERT OR IGNORE INTO photo_versions (photo_id, version_id, name, uri, protected, md5_sum) " +
-					"VALUES (:photo_id, :version_id, :name, :uri, :is_protected, :md5_sum)",
+					"INSERT OR IGNORE INTO photo_versions (photo_id, version_id, name, base_uri, filename, protected, md5_sum) " +
+					"VALUES (:photo_id, :version_id, :name, :base_uri, :filename, :is_protected, :md5_sum)",
 					"photo_id", photo.Id,
 					"version_id", version_id,
 					"name", version.Name,
-					"uri", version.Uri.ToString (),
+			        "base_uri", version.Uri.GetDirectoryUri ().ToString (),
+					"filename", version.Uri.GetFilename (),
 					"is_protected", version.IsProtected,
 					"md5_sum", (version.MD5Sum != String.Empty ? version.MD5Sum : null)));
 			}
@@ -575,10 +581,11 @@ public class PhotoStore : DbStore<Photo> {
 				PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
 				Database.ExecuteNonQuery (new DbCommand (
 					"UPDATE photo_versions SET name = :name, " +
-					"uri = :uri, protected = :protected, md5_sum = :md5_sum " +
+					"base_uri = :base_uri, filename = :filename, protected = :protected, md5_sum = :md5_sum " +
 					"WHERE photo_id = :photo_id AND version_id = :version_id",
 					"name", version.Name,
-					"uri", version.Uri.ToString (),
+					"base_uri", version.Uri.GetDirectoryUri ().ToString (),
+					"filename", version.Uri.GetFilename (),
 					"protected", version.IsProtected,
 					"photo_id", photo.Id,
 					"md5_sum", (version.MD5Sum != String.Empty ? version.MD5Sum : null),
@@ -795,7 +802,7 @@ public class PhotoStore : DbStore<Photo> {
 			
 			if (sql_clause == null || sql_clause.Trim () == String.Empty)
 				continue;
-			
+			Log.Debug ("Type {0} provided clause: {1}", condition.GetType ().ToString (), sql_clause);
 			query_builder.Append (where_added ? " AND " : " WHERE ");
 			query_builder.Append (sql_clause);
 			where_added = true;
@@ -812,7 +819,7 @@ public class PhotoStore : DbStore<Photo> {
 			
 			if (sql_clause == null || sql_clause.Trim () == String.Empty)
 				continue;
-			
+			Log.Debug ("Type {0} provided clause: {1}", condition.GetType ().ToString (), sql_clause);
 			query_builder.Append (order_added ? " , " : "ORDER BY ");
 			query_builder.Append (sql_clause);
 			order_added = true;
@@ -834,6 +841,7 @@ public class PhotoStore : DbStore<Photo> {
 	public void QueryToTemp(string temp_table, string query)
 	{
 		uint timer = Log.DebugTimerStart ();
+		Log.Debug ("Query Started : {0}", query);
 		Database.BeginTransaction ();
 		Database.ExecuteNonQuery (String.Format ("DROP TABLE IF EXISTS {0}", temp_table));
 		//Database.ExecuteNonQuery (String.Format ("CREATE TEMPORARY TABLE {0} AS {1}", temp_table, query));
@@ -869,10 +877,11 @@ public class PhotoStore : DbStore<Photo> {
 			Photo photo = LookupInCache (id);
 
 			if (photo == null) {
-				photo = new Photo (id,
-						   Convert.ToInt64 (reader ["time"]),
-						   new System.Uri (reader ["uri"].ToString ()),
-						   reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null
+				photo =
+					new Photo (id,
+					           Convert.ToInt64 (reader ["time"]),
+					           new Uri (new Uri (reader ["base_uri"].ToString ()), reader ["filename"].ToString ()),
+					           reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null
 				);
 				photo.Description = reader["description"].ToString ();
 				photo.RollId = Convert.ToUInt32 (reader["roll_id"]);
@@ -925,21 +934,43 @@ public class PhotoStore : DbStore<Photo> {
 
 	public Photo [] Query (System.Uri uri)
 	{
-		Log.Debug ("Query Uri {0}", uri);
+		Log.DebugFormat ("Query Uri {0}", uri);
+		
+		/* query by file */
+		if (uri.IsFile) {
+			return Query (new DbCommand (
+			"SELECT id, "			+
+				"time, "			+
+				"base_uri, "		+
+				"filename, "		+
+				"description, "		+
+				"roll_id, "		+
+				"default_version_id, "	+
+				"rating, "		+
+				"md5_sum "		+
+			"FROM photos " 				+
+			"WHERE base_uri LIKE :base_uri "		+
+			"AND filename LIKE :filename",
+			"base_uri", uri.GetDirectoryUri ().ToString (),
+			"filename", uri.GetFilename ()));
+		}
+		
+		/* query by directory */
 		return Query (new DbCommand (
 			"SELECT id, "			+
 				"time, "			+
-				"uri, "			+
+				"base_uri, "		+
+				"filename, "		+
 				"description, "		+
 				"roll_id, "		+
 				"default_version_id, "	+
 				"rating, "		+
 				"md5_sum "		+
 			"FROM photos " 				+
-			"WHERE uri LIKE :uri "		+
-			"AND uri NOT LIKE :uri_",
-			"uri", uri.ToString () + "%",
-			"uri_", uri.ToString () + "/%/%"));
+			"WHERE base_uri LIKE :base_uri "		+
+			"AND base_uri NOT LIKE :base_uri_",
+			"base_uri", uri.ToString () + "%",
+			"base_uri_", uri.ToString () + "/%/%"));
 	}
 
 	[Obsolete ("drop this, use IQueryCondition correctly instead")]
@@ -986,7 +1017,8 @@ public class PhotoStore : DbStore<Photo> {
 		ArrayList where_clauses = new ArrayList ();
 		query_builder.Append ("SELECT id, " 			+
 					     "time, "			+
-					     "uri, "			+
+					     "base_uri, "			+
+					     "filename, "			+
 					     "description, "		+
 					     "roll_id, "   		+
 					     "default_version_id, "	+
@@ -1058,7 +1090,8 @@ public class PhotoStore : DbStore<Photo> {
 		ArrayList where_clauses = new ArrayList ();
 		query_builder.Append ("SELECT id, " 			+
 					     "time, "			+
-					     "uri, "			+
+					     "base_uri, "			+
+					     "filename, "			+
 					     "description, "		+
 				      	     "roll_id, "   		+
 					     "default_version_id, "	+
diff --git a/src/Preferences.cs b/src/Preferences.cs
index 29c5803..6290532 100644
--- a/src/Preferences.cs
+++ b/src/Preferences.cs
@@ -51,7 +51,6 @@ namespace FSpot
 		public const string TAG_ICON_AUTOMATIC = APP_FSPOT + "ui/tag_icon_automatic";
 		
 		public const string GLASS_POSITION = APP_FSPOT + "ui/glass_position";
-		public const string GROUP_ADAPTOR = APP_FSPOT + "ui/group_adaptor";
 		public const string GROUP_ADAPTOR_ORDER_ASC = APP_FSPOT + "ui/group_adaptor_sort_asc";
 		
 		public const string SIDEBAR_POSITION = APP_FSPOT + "ui/sidebar_size";
@@ -118,7 +117,6 @@ namespace FSpot
 			case GROUP_ADAPTOR_ORDER_ASC:
 				return false;
 
-			case GROUP_ADAPTOR:
 			case GLASS_POSITION:
 				return null;
 
diff --git a/src/Query/FolderSet.cs b/src/Query/FolderSet.cs
new file mode 100644
index 0000000..70a13ea
--- /dev/null
+++ b/src/Query/FolderSet.cs
@@ -0,0 +1,54 @@
+/*
+ * FSpot.Query.FolderSet
+ *
+ * Author(s):
+ *	Mike Gemuende  <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+
+using System;
+using System.Collections.Generic;
+
+
+namespace FSpot.Query
+{
+	public class FolderSet : IQueryCondition
+	{
+		HashSet<Uri> uri_list;
+		
+		public FolderSet ()
+		{
+			uri_list = new HashSet<Uri> ();
+		}
+		
+		public IEnumerable<Uri> Folders {
+			get { return uri_list; }
+			set { uri_list = (value == null) ? new HashSet<Uri> () : new HashSet<Uri> (value); }
+		}
+		
+		protected static string EscapeQuotes (string v)
+		{
+			return v == null ? String.Empty : v.Replace("'", "''");
+		}
+		
+		public string SqlClause ()
+		{
+			string[] items = new string [uri_list.Count];
+			
+			if (items.Length == 0)
+				return null;
+			
+			int i = 0;
+			foreach (Uri uri in uri_list) {
+				items[i] =
+					String.Format ("id IN (SELECT id FROM photos WHERE base_uri LIKE '{0}%')",
+					               EscapeQuotes (uri.ToString ()));
+				i++;
+			}
+			
+			return String.Join (" OR ", items);
+		}
+	}
+}
diff --git a/src/Query/LogicalTerm.cs b/src/Query/LogicalTerm.cs
index 9a4640c..f9d0b23 100644
--- a/src/Query/LogicalTerm.cs
+++ b/src/Query/LogicalTerm.cs
@@ -190,7 +190,7 @@ namespace FSpot.Query
 				foreach (LogicalTerm t in (term as AndTerm).terms)
 					Add (t);
 			else
-				terms.Add (term);			
+				terms.Add (term);
 		}
 
 		public override string SqlClause ()
diff --git a/src/Query/OrderByTime.cs b/src/Query/OrderByTime.cs
index a1a891e..3383fd0 100644
--- a/src/Query/OrderByTime.cs
+++ b/src/Query/OrderByTime.cs
@@ -29,7 +29,7 @@ namespace FSpot.Query {
 
 		public string SqlClause ()
 		{
-			return String.Format (" time {0}, uri {0} ", asc ? "ASC" : "DESC");
+			return String.Format (" time {0}, filename {0} ", asc ? "ASC" : "DESC");
 		}
 	}
 }
diff --git a/src/QueryWidget.cs b/src/QueryWidget.cs
index 59b72ef..ee4c7e1 100644
--- a/src/QueryWidget.cs
+++ b/src/QueryWidget.cs
@@ -7,17 +7,29 @@
  * This is free software. See COPYING for details.
  */
 
-using FSpot.Query;
-using FSpot.Widgets;
+
+using System;
+using System.Collections.Generic;
+
 using Mono.Unix;
+
 using Gtk;
 
+using FSpot.Gui;
+using FSpot.Utils;
+using FSpot.Query;
+using FSpot.Widgets;
+
+
+
 namespace FSpot {
 
 	public class QueryWidget : HighlightedBox {
 		PhotoQuery query;
 		LogicWidget logic_widget;
-	        Gtk.HBox box;
+		FolderQueryWidget folder_query_widget;
+		
+		Gtk.HBox box;
 		Gtk.Label label;
 		Gtk.Label untagged;
 		Gtk.Label rated;
@@ -70,6 +82,10 @@ namespace FSpot {
 			rollfilter.Visible = false;
 			box.PackStart (rollfilter, false, false, 0);
 
+			folder_query_widget = new FolderQueryWidget ();
+			folder_query_widget.Visible = false;
+			box.PackStart (folder_query_widget, false, false, 0);
+			
 			logic_widget = new LogicWidget (query, db.Tags);
 			logic_widget.Show ();
 			box.PackStart (logic_widget, true, true, 0);
@@ -127,6 +143,10 @@ namespace FSpot {
 			query.RatingRange = null;
 			logic_widget.Clear = true;
 			logic_widget.UpdateQuery ();
+			
+			folder_query_widget.Clear ();
+			query.RequestReload ();
+			
 			HideBar ();
 		}
 
@@ -147,11 +167,14 @@ namespace FSpot {
 			if (query.ExtraCondition == null)
 				logic_widget.Clear = true;
 
-			if (!logic_widget.Clear || query.Untagged || (query.RollSet != null) || (query.RatingRange != null)) {
-		                ShowBar ();
-			} else {
+			if ( ! logic_widget.Clear
+			    || query.Untagged
+			    || (query.RollSet != null)
+			    || (query.RatingRange != null)
+			    || folder_query_widget.Visible)
+				ShowBar ();
+			else
 				HideBar ();
-			}
 
 			untagged.Visible = query.Untagged;
 			rated.Visible = (query.RatingRange != null);
@@ -197,5 +220,11 @@ namespace FSpot {
 		{
 			return logic_widget.TagRequired (tag);
 		}
+		
+		public void SetFolders (IEnumerable<Uri> uri_list)
+		{
+			folder_query_widget.SetFolders (uri_list);
+			query.RequestReload ();
+		}
 	}
 }
diff --git a/src/Term.cs b/src/Term.cs
index 8c9c5f2..f6321c3 100644
--- a/src/Term.cs
+++ b/src/Term.cs
@@ -944,7 +944,7 @@ namespace FSpot {
 		public override string SqlCondition ()
 		{
 			return String.Format (
-				       "id {0}IN (SELECT id FROM photos WHERE uri LIKE '%{1}%' OR description LIKE '%{1}%')",
+				       "id {0}IN (SELECT id FROM photos WHERE base_uri LIKE '%{1}%' OR filename LIKE '%{1}%' OR description LIKE '%{1}%')",
 				       (IsNegated ? "NOT " : ""), EscapeQuotes(text)
 			       );
 		}
diff --git a/src/Updater.cs b/src/Updater.cs
index eedc967..0718577 100644
--- a/src/Updater.cs
+++ b/src/Updater.cs
@@ -494,6 +494,94 @@ namespace FSpot.Database {
 			//AddUpdate (new Version (14,0), delegate () {
 			//	do update here
 			//});
+			
+			// Update to version 17.0, split uri and filename
+			AddUpdate (new Version (17,0),delegate () {
+				string tmp_photos = MoveTableToTemp ("photos");
+				string tmp_versions = MoveTableToTemp ("photo_versions");
+				
+				Execute (
+					"CREATE TABLE photos (\n" +
+					"	id			INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \n" +
+					"	time			INTEGER NOT NULL, \n" +
+					"	base_uri		STRING NOT NULL, \n" +
+					"	filename		STRING NOT NULL, \n" +
+					"	description		TEXT NOT NULL, \n" +
+					"	roll_id			INTEGER NOT NULL, \n" +
+					"	default_version_id	INTEGER NOT NULL, \n" +
+					"	rating			INTEGER NULL, \n" +
+					"	md5_sum			TEXT NULL\n" +
+					")");
+				
+				Execute (
+					"CREATE TABLE photo_versions (\n"+
+					"	photo_id	INTEGER, \n" +
+					"	version_id	INTEGER, \n" +
+					"	name		STRING, \n" +
+					"	base_uri		STRING NOT NULL, \n" +
+					"	filename		STRING NOT NULL, \n" +
+					"	md5_sum		TEXT NULL, \n" +
+					"	protected	BOOLEAN, \n" +
+					"	UNIQUE (photo_id, version_id)\n" +
+					")");
+				
+				SqliteDataReader reader = ExecuteReader (String.Format (
+					"SELECT id, time, uri, description, roll_id, default_version_id, rating, md5_sum " +
+					"FROM {0} ", tmp_photos));
+		
+				while (reader.Read ()) {
+					System.Uri photo_uri = new System.Uri (reader ["uri"] as string);
+					
+					string filename = photo_uri.GetFilename ();
+					Uri base_uri = photo_uri.GetDirectoryUri ();
+
+					string md5 = reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null;
+					
+					Execute (new DbCommand (
+						"INSERT INTO photos (id, time, base_uri, filename, description, roll_id, default_version_id, rating, md5_sum) "	+
+						"VALUES (:id, :time, :base_uri, :filename, :description, :roll_id, :default_version_id, :rating, :md5_sum)",
+						"id", Convert.ToUInt32 (reader ["id"]),
+						"time", Convert.ToUInt32 (reader ["time"]),
+						"base_uri", base_uri.ToString (),
+						"filename", filename,
+						"description", reader["description"].ToString (),
+						"roll_id", Convert.ToUInt32 (reader ["roll_id"]),
+						"default_version_id", Convert.ToUInt32 (reader ["default_version_id"]),
+						"rating", Convert.ToUInt32 (reader ["rating"]),
+						"md5_sum", String.IsNullOrEmpty (md5) ? null : md5));
+				}
+				
+				reader.Close ();
+				
+				reader = ExecuteReader (String.Format (
+						"SELECT photo_id, version_id, name, uri, md5_sum, protected " +
+						"FROM {0} ", tmp_versions));
+				
+				while (reader.Read ()) {
+					System.Uri photo_uri = new System.Uri (reader ["uri"] as string);
+					
+					string filename = photo_uri.GetFilename ();
+					Uri base_uri = photo_uri.GetDirectoryUri ();
+
+					string md5 = reader["md5_sum"] != null ? reader ["md5_sum"].ToString () : null;
+					
+					Execute (new DbCommand (				
+						"INSERT INTO photo_versions (photo_id, version_id, name, base_uri, filename, protected, md5_sum) " +
+						"VALUES (:photo_id, :version_id, :name, :base_uri, :filename, :is_protected, :md5_sum)",
+						"photo_id", Convert.ToUInt32 (reader ["photo_id"]),
+						"version_id", Convert.ToUInt32 (reader ["version_id"]),
+						"name", reader["name"].ToString (),
+						"base_uri", base_uri.ToString (),
+						"filename", filename,
+						"is_protected", Convert.ToBoolean (reader["protected"]),
+						"md5_sum", String.IsNullOrEmpty (md5) ? null : md5));
+				}
+				
+				Execute ("CREATE INDEX idx_photos_roll_id ON photos(roll_id)");
+				Execute ("CREATE INDEX idx_photo_versions_id ON photo_versions(photo_id)");
+				
+
+			}, true);
 		}
 
 		public static void Run (Db database)
@@ -549,7 +637,7 @@ namespace FSpot.Database {
 				}
 
 				db.CommitTransaction ();
-			} catch (Exception e) {
+			} catch (Exception e) {Log.DebugException (e);
 				Log.Warning ("Rolling back database changes because of Exception");
 				// There was an error, roll back the database
 				db.RollbackTransaction ();
diff --git a/src/Utils/UriUtils.cs b/src/Utils/UriUtils.cs
index 15b78d4..93580f6 100644
--- a/src/Utils/UriUtils.cs
+++ b/src/Utils/UriUtils.cs
@@ -14,7 +14,7 @@ using System.IO;
 namespace FSpot.Utils
 {
 	public static class UriUtils
-	{
+	{		
 		public static string UriToStringEscaped (Uri uri)
 		{
 			return EscapeString (uri.ToString (), false, true, false);
diff --git a/src/Widgets/FolderTreePage.cs b/src/Widgets/FolderTreePage.cs
new file mode 100644
index 0000000..9dd3c6c
--- /dev/null
+++ b/src/Widgets/FolderTreePage.cs
@@ -0,0 +1,539 @@
+/*
+ * FSpot.Widgets.FolderTreePage.cs
+ *
+ * Author(s)
+ * 	Mike Gemuende <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+
+using System;
+using System.Collections.Generic;
+
+using Gtk;
+
+using GLib;
+
+using FSpot;
+using FSpot.Gui;
+using FSpot.Utils;
+
+using Banshee.Database;
+
+using Mono.Unix;
+using Mono.Data.SqliteClient;
+
+
+
+namespace FSpot.Widgets
+{
+	
+	
+	public class FolderTreePage : SidebarPage
+	{
+		private readonly FolderTreeWidget folder_tree_widget;
+		
+		
+		
+		public FolderTreePage () 
+			: base (new ScrolledWindow (), Catalog.GetString ("Folders"), "gtk-directory")
+		{
+			ScrolledWindow scrolled_window = SidebarWidget as ScrolledWindow;
+			folder_tree_widget = new FolderTreeWidget ();
+			scrolled_window.Add (folder_tree_widget);
+		}
+		
+		protected override void AddedToSidebar () {
+		}
+	}
+	
+	public class FolderTreeWidget : SaneTreeView
+	{	
+		FolderTreeModel folder_tree_model;
+	
+		public FolderTreeWidget () : this (new FolderTreeModel ())
+		{
+		}
+
+		public FolderTreeWidget (FolderTreeModel tree_model) : base (tree_model)
+		{		
+			folder_tree_model = tree_model;
+			
+			HeadersVisible = false;
+			
+			TreeViewColumn column = new TreeViewColumn ();
+			
+			CellRendererPixbuf pixbuf_renderer = new CellRendererPixbuf ();
+			column.PackStart (pixbuf_renderer, false);
+			column.SetCellDataFunc (pixbuf_renderer, PixbufDataFunc as TreeCellDataFunc);
+			
+			CellRendererTextProgress folder_renderer = new CellRendererTextProgress ();
+			column.PackStart (folder_renderer, true);
+			column.SetCellDataFunc (folder_renderer, FolderDataFunc as TreeCellDataFunc);
+			
+			AppendColumn (column);
+			
+			Gtk.Drag.SourceSet (this, Gdk.ModifierType.Button1Mask | Gdk.ModifierType.Button3Mask,
+				    folder_tree_source_target_table, Gdk.DragAction.Copy | Gdk.DragAction.Move);
+		}
+		
+		public UriList SelectedUris {
+			get {
+				UriList list = new UriList ();
+				
+				TreePath[] selected_rows = Selection.GetSelectedRows ();
+				
+				foreach (TreePath row in selected_rows)
+					list.Add (folder_tree_model.GetUriByPath (row));
+				
+				return list;
+			}
+		}
+		
+		private void PixbufDataFunc (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
+		{
+			CellRendererPixbuf renderer = cell as CellRendererPixbuf;
+			string text = folder_tree_model.GetFolderNameByIter (iter);
+			
+			string stock;
+			File file = FileFactory.NewForUri (folder_tree_model.GetUriByIter (iter));
+			try {
+				FileInfo info =
+					file.QueryInfo ("standard::icon", FileQueryInfoFlags.None, null);
+				
+				ThemedIcon themed_icon = info.Icon as ThemedIcon;
+				if (themed_icon != null && themed_icon.Names.Length > 0)
+					stock = themed_icon.Names[0];
+				else
+					stock = "gtk-directory";
+				
+			} catch (Exception e) {
+				stock = "gtk-directory";
+			}
+
+			TreeIter tmp;
+			if (tree_model.IterParent (out tmp, iter)) {
+				renderer.IconName = stock;
+				renderer.CellBackground = null;
+			} else {
+				renderer.IconName = stock;
+				renderer.CellBackgroundGdk = Style.Background (StateType.Selected);
+			}
+		}
+		
+		private void FolderDataFunc (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
+		{
+			CellRendererTextProgress renderer = cell as CellRendererTextProgress;
+			
+			int progress_value = 0;
+			int count = (tree_model as FolderTreeModel).Count;
+			
+			if (count != 0)
+				progress_value = (int) ((100.0 * folder_tree_model.GetPhotoCountByIter (iter)) / count);
+			
+			renderer.Value = progress_value;
+			
+			string text = folder_tree_model.GetFolderNameByIter (iter);
+			
+			TreeIter tmp;
+			if (tree_model.IterParent (out tmp, iter)) {
+				renderer.UseMarkup = false;
+				renderer.Text = text;
+				renderer.CellBackground = null;
+			} else {
+				renderer.UseMarkup = true;
+				
+				/* since import do not use GIO at the moment, no other prefix than file:/// is
+				 * possible.
+				 */
+				if (text == Uri.UriSchemeFile)
+					renderer.Text = String.Format ("<b>{0}</b>", Catalog.GetString ("Filesystem"));
+				else
+					renderer.Text = String.Format ("<b>{0}</b>", text);
+				
+				renderer.CellBackgroundGdk = Style.Background (StateType.Selected);
+			}
+		}
+			
+		private string GetStock (string scheme)
+		{
+			/* not very usefull at the moment */
+			if (scheme == Uri.UriSchemeFile)
+				return "gtk-directory";
+		
+			return "gtk-directory";
+		}
+		
+		private static TargetEntry [] folder_tree_source_target_table =
+			new TargetEntry [] {
+				DragDropTargets.UriQueryEntry,
+				DragDropTargets.UriListEntry,
+				DragDropTargets.PlainTextEntry
+		};
+		
+		
+		protected override void OnDragDataGet (Gdk.DragContext context, Gtk.SelectionData selection_data, uint info, uint time_)
+		{
+			if (info == DragDropTargets.UriQueryEntry.Info
+			    || info == DragDropTargets.UriListEntry.Info
+			    || info == DragDropTargets.PlainTextEntry.Info) {
+				
+				selection_data.SetUriListData (SelectedUris, context.Targets[0]);
+				return;
+			}
+		}
+		
+		protected override bool OnDragDrop (Gdk.DragContext context, int x, int y, uint time_)
+		{
+			return true;
+		}
+		
+		protected override void OnRowActivated (Gtk.TreePath path, Gtk.TreeViewColumn column)
+		{
+			MainWindow.Toplevel.SetFolderQuery (SelectedUris);	
+		}
+
+	}
+
+	
+	/*
+	 * Because subclassing of CellRendererText does not to work, we
+	 * use a new cellrenderer, which renderes a simple text and a
+	 * progress bar below the text similar to the one used in baobab (gnome-utils)
+	 */
+	public class CellRendererTextProgress : CellRenderer
+	{
+		readonly int progress_width;
+		readonly int progress_height;
+		
+		Gdk.Color green;
+		Gdk.Color yellow;
+		Gdk.Color red;
+		
+		public CellRendererTextProgress (int progress_width, int progress_height)
+		{
+			this.progress_width = progress_width;
+			this.progress_height = progress_height;
+			
+			Xalign = 0.0f;
+			Yalign = 0.5f;
+			
+			Xpad = 2;
+			Ypad = 2;
+			
+			green = new Gdk.Color (0xcc, 0x00, 0x00);
+			yellow = new Gdk.Color (0xed, 0xd4, 0x00);
+			red = new Gdk.Color (0x73, 0xd2, 0x16);
+		}
+		
+		public CellRendererTextProgress () : this (70, 8)
+		{
+		}
+		
+		int progress_value;
+		
+		[GLib.PropertyAttribute ("value")]
+		public int Value {
+			get { return progress_value; }
+			set {
+				/* normalize value */
+				progress_value = Math.Max (Math.Min (value, 100), 0);
+			}
+		}
+		
+		Pango.Layout text_layout;
+		string text;
+		
+		[GLib.PropertyAttribute ("text")]
+		public string Text {
+			get { return text; }
+			set {
+				if (text == value)
+					return;
+				
+				text = value;
+				text_layout = null;	
+			}
+		}
+		
+		bool use_markup;
+		public bool UseMarkup {
+			get { return use_markup; }
+			set {
+				if (use_markup == value)
+					return;
+				
+				use_markup = value;
+				text_layout = null;
+			}
+		}
+		
+		private void UpdateLayout (Widget widget)
+		{
+			text_layout = new Pango.Layout (widget.PangoContext);
+
+			if (UseMarkup)
+				text_layout.SetMarkup (text);
+			else
+				text_layout.SetText (text);
+		}
+		
+		private Gdk.Color GetValueColor ()
+		{
+			if (progress_value <= 33)
+				return green;
+			
+			if (progress_value <= 66)
+				return yellow;
+			
+			return red;
+		}
+		
+		public override void GetSize (Gtk.Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+		{
+			if (text_layout == null)
+				UpdateLayout (widget);
+			
+			int text_width, text_height;
+			
+			text_layout.GetPixelSize (out text_width, out text_height);
+			
+			width = (int) (2 * Xpad + Math.Max (progress_width, text_width));
+			height = (int) (3 * Ypad + progress_height + text_height);
+			
+			x_offset = Math.Max ((int) (Xalign * (cell_area.Width - width)), 0);
+			y_offset = Math.Max ((int) (Yalign * (cell_area.Height - height)), 0);
+		}
+		
+		protected override void Render (Gdk.Drawable window, Gtk.Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, Gtk.CellRendererState flags)
+		{
+			base.Render (window, widget, background_area, cell_area, expose_area, flags);
+			
+			if (text_layout == null)
+				UpdateLayout (widget);
+
+			int x, y, width, height, text_width, text_height;
+			
+			/* first render the text */
+			text_layout.GetPixelSize (out text_width, out text_height);
+			
+			x  = (int) (cell_area.X + Xpad + Math.Max ((int) (Xalign * (cell_area.Width - 2 * Xpad - text_width)), 0));
+			y  = (int) (cell_area.Y + Ypad);
+
+			Style.PaintLayout (widget.Style,
+			                   window,
+			                   StateType.Normal,
+			                   true,
+			                   cell_area,
+			                   widget,
+			                   "cellrenderertextprogress",
+			                   x, y,
+			                   text_layout);
+			
+			y += (int) (text_height + Ypad);
+			x  = (int) (cell_area.X + Xpad + Math.Max ((int) (Xalign * (cell_area.Width - 2 * Xpad - progress_width)), 0));
+			
+			
+			/* second render the progress bar */
+			
+			/* dispose cairo object after usage */
+			using (Cairo.Context cairo_context = Gdk.CairoHelper.Create (window)) {
+				
+				width = progress_width;
+				height = progress_height;
+				
+				cairo_context.Rectangle (x, y, width, height);
+				Gdk.CairoHelper.SetSourceColor (cairo_context, widget.Style.Dark (StateType.Normal));
+				cairo_context.Fill ();
+				
+				x += widget.Style.XThickness;
+				y += widget.Style.XThickness;
+				width -= 2* widget.Style.XThickness;
+				height -= 2 * widget.Style.Ythickness;
+				
+				cairo_context.Rectangle (x, y, width, height);
+				Gdk.CairoHelper.SetSourceColor (cairo_context, widget.Style.Light (StateType.Normal));
+				cairo_context.Fill ();
+				
+				/* scale the value and ensure, that at least one pixel is drawn, if the value is greater than zero */
+				int scaled_width =
+					(int) Math.Max (((progress_value * width) / 100.0),
+					                (progress_value == 0)? 0 : 1);
+				
+				cairo_context.Rectangle (x, y, scaled_width, height);
+				Gdk.CairoHelper.SetSourceColor (cairo_context, GetValueColor ());
+				cairo_context.Fill ();
+			}
+		}
+
+	}
+	
+	
+	public class FolderTreeModel : TreeStore
+	{
+		Db database;
+		
+		const string query_string = 
+			"SELECT base_uri, COUNT(*) AS count " +
+			"FROM photos " + 
+			"GROUP BY base_uri " +
+			"ORDER BY base_uri DESC";
+		
+		
+		public FolderTreeModel ()
+			: base (typeof (string), typeof (int), typeof (Uri))
+		{
+			database = MainWindow.Toplevel.Database;
+			database.Photos.ItemsChanged += HandlePhotoItemsChanged;
+			
+			UpdateFolderTree ();
+		}
+		
+		void HandlePhotoItemsChanged (object sender, DbItemEventArgs<Photo> e)
+		{
+			UpdateFolderTree ();
+		}
+		
+		public string GetFolderNameByIter (TreeIter iter)
+		{
+			if ( ! IterIsValid (iter))
+				return null;
+			
+			return (string) GetValue (iter, 0);
+		}
+		
+		public int GetPhotoCountByIter (TreeIter iter)
+		{
+			if ( ! IterIsValid (iter))
+				return -1;
+			
+			return (int) GetValue (iter, 1);
+		}
+		
+		public Uri GetUriByIter (TreeIter iter)
+		{
+			if ( ! IterIsValid (iter))
+				return null;
+			
+			return (Uri) GetValue (iter, 2);
+		}	
+		
+		public Uri GetUriByPath (TreePath row)
+		{
+			TreeIter iter;
+			
+			GetIter (out iter, row);
+			
+			return GetUriByIter (iter);
+		}
+		
+		int count_all;
+		public int Count {
+			get { return count_all; }
+		}
+		
+		/*
+		 * UpdateFolderTree queries for directories in database and updates
+		 * a possibly existing folder-tree to the queried structure
+		 */
+		private void UpdateFolderTree ()
+		{
+			Clear ();
+			
+			count_all = 0;
+			
+			/* points at start of each iteration to the leaf of the last inserted uri */
+			TreeIter iter = TreeIter.Zero;
+			
+			/* stores the segments of the last inserted uri */
+			string[] last_segments = new string[] {};
+			
+			int last_count = 0;
+			
+			SqliteDataReader reader = database.Database.Query (query_string);
+			
+			while (reader.Read ()) {
+				Uri base_uri = new Uri (reader["base_uri"].ToString ());
+				
+				if ( ! base_uri.IsAbsoluteUri) {
+					FSpot.Utils.Log.Error ("Uri must be absolute: {0}", base_uri.ToString ());
+					continue;
+				}
+				
+				int count = Convert.ToInt32 (reader["count"]);
+				
+				string[] segments = base_uri.Segments;
+
+				/* 
+				 * since we have an absolute uri, first segement starts with "/" according
+				 * to the msdn doc. So we can overwrite the first segment for our needs and
+				 * put the scheme here.
+				 */
+				segments[0] = base_uri.Scheme;
+				
+				int i = 0;
+				
+				/* find first difference of last inserted an current uri */
+				while (i < last_segments.Length && i < segments.Length) {
+					
+					/* remove suffix '/', which are appended to every directory (see msdn-doc) */
+					segments[i] = segments[i].TrimEnd ('/');
+					
+					if (segments[i] != last_segments[i])
+						break;
+					
+					i++;
+				}
+				
+				/* points to the parent node of the current iter */
+				TreeIter parent_iter = iter;
+				
+				/* step back to the level, where the difference occur */
+				for (int j = 0; j + i < last_segments.Length; j++) {
+					
+					iter = parent_iter;
+					
+					if (IterParent (out parent_iter, iter)) { 
+						last_count += (int)GetValue (parent_iter, 1);
+						SetValue (parent_iter, 1, last_count);
+					} else
+						count_all += (int)last_count;
+				}
+				
+				while (i < segments.Length) {
+					segments[i] = segments[i].TrimEnd ('/');
+					
+					if (IterIsValid (parent_iter))
+						iter =
+							AppendValues (parent_iter,
+							              segments[i],
+							              (segments.Length - 1 == i)? count : 0,
+							              new Uri ((Uri) GetValue (parent_iter, 2),
+							                       String.Format ("{0}/", segments[i]))
+							              );
+					else
+						iter =
+							AppendValues (segments[i],
+							              (segments.Length - 1 == i)? count : 0,
+							              new Uri (base_uri, "/"));
+					
+					parent_iter = iter;
+					
+					i++;
+				}
+				
+				last_count = count;
+				last_segments = segments;
+				
+			}
+			
+			/* and at least, step back and update photo count */
+			while (IterParent (out iter, iter)) {
+				last_count += (int)GetValue (iter, 1);
+				SetValue (iter, 1, last_count);
+			}
+			count_all += (int)last_count;
+		}
+	}
+}
diff --git a/src/ui/main_window.ui b/src/ui/main_window.ui
index 1a08198..c04452c 100644
--- a/src/ui/main_window.ui
+++ b/src/ui/main_window.ui
@@ -373,22 +373,6 @@
           </object>
         </child>
         <child>
-          <object class="GtkRadioAction" id="month">
-            <property name="active">True</property>
-            <property name="name">month</property>
-            <property name="label" translatable="yes">_Month</property>
-            <signal handler="HandleArrangeByTime" name="activate"/>
-          </object>
-        </child>
-        <child>
-          <object class="GtkRadioAction" id="directory">
-            <property name="group">month</property>
-            <property name="name">directory</property>
-            <property name="label" translatable="yes">_Folder</property>
-            <signal handler="HandleArrangeByDirectory" name="activate"/>
-          </object>
-        </child>
-        <child>
           <object class="GtkToggleAction" id="reverse_order">
             <property name="name">reverse_order</property>
             <property name="label" translatable="yes">_Reverse Order</property>
@@ -638,10 +622,6 @@
             <menuitem action="tag_icon_large"/>
           </menu>
           <separator/>
-          <menu action="arranged_by">
-            <menuitem action="month"/>
-            <menuitem action="directory"/>
-          </menu>
           <menuitem action="reverse_order"/>
         </menu>
         <menu action="find">



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