[f-spot: 2/3] initial commit



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

    initial commit
    - new widget for folder view created
    - database version pushed to 17, uri now stored as base_uri and filename
    
    using all added IQueryCondition for PhotoQuery
    Fix for PhotoStore QueryToTemp (string, params IQueryCondition[])
    
    don't duplicate code
    
    use at least some few theme colours for ProgressBar
    
    call correct function
    
    fix uri to base_uri and filename
    
    add FolderQueryWidget
    
    added dragndrop and searching for folders
    
    use UriExtension.cs class
    
    adding UriExtensions and a fix to keep / at the end for directories
    
    need to find a better way to update the treeview, but this have to
    last for the first time.
    
    some little fixups
    
    add UriExtensions to Makefile
    
    use all selected uris, for a double click too
    
    handle PhotoStore changes
    
    change query to base_uri and filename
    
    switching to GtkBuilder for Mainwindow
    
    removing context menu for viewing directories in "timeline"
    
    added main_window.ui to resources
    
    make zoom slider workable again
    
    dispose cairo context after usage
    
    remove FolderAdaptor
    
    make own IQueryCondition for folders
    
    add own IQueryCondition for folders
    
    remove of obselete method usage
    
    little change
    
    remove DirectoryAdaptor.cs
    
    more compact display of multiple folders
    
    usage of override On-methods instead of handlers
    
    fix merge
    
    remove unused stuff
    
    add tooltips to zoom-icons again (after glade removed them)
    
    some fixups
    
    display scheme, if other than file://

 src/DirectoryAdaptor.cs       |  131 ----------
 src/DirectoryCollection.cs    |   52 ----
 src/FSpot.addin.xml           |    1 +
 src/FolderQueryWidget.cs      |  113 +++++++++
 src/GroupSelector.cs          |   11 -
 src/MainWindow.cs             |   56 +----
 src/Makefile.am               |    5 +-
 src/PhotoQuery.cs             |    1 +
 src/PhotoStore.cs             |  140 +++++++----
 src/Preferences.cs            |    2 -
 src/Query/FolderSet.cs        |   77 ++++++
 src/Query/LogicalTerm.cs      |    2 +-
 src/QueryWidget.cs            |   42 +++-
 src/Term.cs                   |    2 +-
 src/Updater.cs                |   90 +++++++-
 src/Utils/UriUtils.cs         |    2 +-
 src/Widgets/FolderTreePage.cs |  536 +++++++++++++++++++++++++++++++++++++++++
 src/Widgets/SaneTreeView.cs   |    5 +
 18 files changed, 958 insertions(+), 310 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..862dd2d
--- /dev/null
+++ b/src/FolderQueryWidget.cs
@@ -0,0 +1,113 @@
+/*
+ * 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 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.UriList.Count;
+			
+			if (length == 0) {
+				Hide ();
+				return;
+			}
+			
+			if (length < 4) {
+				
+				foreach (Uri uri in folder_set.UriList) {
+					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.UriList) {
+					if (builder.Length > 0)
+						builder.AppendLine ();
+					
+					builder.Append (uri.ToString ());
+				}
+				
+				TooltipText = builder.ToString ();
+			}
+			
+			ShowAll ();
+		}
+		
+		public void SetFolders (UriList uri_list)
+		{
+			folder_set.UriList = uri_list;
+			
+			UpdateGui ();
+		}
+		
+		public void Clear ()
+		{
+			folder_set.UriList = 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..d021f15 100644
--- a/src/MainWindow.cs
+++ b/src/MainWindow.cs
@@ -477,7 +477,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 +932,12 @@ public class MainWindow {
 		}
 	}
 	
+	public void SetFolderQuery (UriList uri_list)
+	{
+		ShowQueryWidget ();
+		query_widget.SetFolders (uri_list);
+	}
+	
 	public void AddTagsQuery (Tag [] tags)
 	{
 		ShowQueryWidget ();
@@ -976,12 +981,12 @@ public class MainWindow {
 		//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))
+		/*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));*/
 	}
 
 	private void JumpTo (int index)
@@ -1618,44 +1623,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 +1682,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 +2501,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..1b9bf9f 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),
@@ -786,8 +793,10 @@ public class PhotoStore : DbStore<Photo> {
 
 		bool where_added = false;
 		foreach (IQueryCondition condition in conditions) {
+			
 			if (condition == null)
 				continue;
+			
 			if (condition is IOrderCondition)
 				continue;
 			
@@ -795,7 +804,6 @@ public class PhotoStore : DbStore<Photo> {
 			
 			if (sql_clause == null || sql_clause.Trim () == String.Empty)
 				continue;
-			
 			query_builder.Append (where_added ? " AND " : " WHERE ");
 			query_builder.Append (sql_clause);
 			where_added = true;
@@ -805,6 +813,7 @@ public class PhotoStore : DbStore<Photo> {
 		foreach (IQueryCondition condition in conditions) {
 			if (condition == null)
 				continue;
+			
 			if (!(condition is IOrderCondition))
 				continue;
 			
@@ -812,7 +821,6 @@ public class PhotoStore : DbStore<Photo> {
 			
 			if (sql_clause == null || sql_clause.Trim () == String.Empty)
 				continue;
-			
 			query_builder.Append (order_added ? " , " : "ORDER BY ");
 			query_builder.Append (sql_clause);
 			order_added = true;
@@ -834,6 +842,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 +878,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 +935,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 +1018,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 +1091,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..4917311
--- /dev/null
+++ b/src/Query/FolderSet.cs
@@ -0,0 +1,77 @@
+/*
+ * FSpot.Query.FolderSet
+ *
+ * Author(s):
+ *	Mike Gemuende  <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+
+using System;
+using System.Collections;
+
+
+namespace FSpot.Query
+{
+	
+	
+	public class FolderSet : IQueryCondition
+	{
+		/* we use ArrayList, because UriList is located in the f-spot.exe
+		 * assembly, which is (not yet) available here
+		 */
+		ArrayList uri_list;
+		
+		public FolderSet ()
+		{
+			uri_list = new ArrayList ();
+		}
+		
+		private bool AddFolderInternal (Uri uri)
+		{
+			if (uri_list.Contains (uri))
+				return false;
+			
+			uri_list.Add (uri);
+			
+			return true;
+		}
+		
+		public ArrayList UriList {
+			get { return uri_list; }
+			set {
+				uri_list = new ArrayList ();
+				
+				if (value == null)
+					return;
+				
+				foreach (Uri uri in value)
+					AddFolderInternal (uri);
+			}
+		}
+		
+		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/QueryWidget.cs b/src/QueryWidget.cs
index 59b72ef..dd24f16 100644
--- a/src/QueryWidget.cs
+++ b/src/QueryWidget.cs
@@ -7,17 +7,28 @@
  * This is free software. See COPYING for details.
  */
 
-using FSpot.Query;
-using FSpot.Widgets;
+
+using System;
+
 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 +81,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 +142,10 @@ namespace FSpot {
 			query.RatingRange = null;
 			logic_widget.Clear = true;
 			logic_widget.UpdateQuery ();
+			
+			folder_query_widget.Clear ();
+			query.RequestReload ();
+			
 			HideBar ();
 		}
 
@@ -147,11 +166,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 +219,11 @@ namespace FSpot {
 		{
 			return logic_widget.TagRequired (tag);
 		}
+		
+		public void SetFolders (UriList 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..b75be9d 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"].ToString ();
+					
+					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", (md5 != String.Empty ? md5 : null)));
+				}
+				
+				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"].ToString ();
+					
+					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", (md5 != String.Empty ? md5 : null)));
+				}
+				
+				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..9eb3e71
--- /dev/null
+++ b/src/Widgets/FolderTreePage.cs
@@ -0,0 +1,536 @@
+/*
+ * 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 ()
+		{		
+			folder_tree_model = new FolderTreeModel ();
+			Model = folder_tree_model;
+			
+			HeadersVisible = false;
+			
+			TreeViewColumn column = new TreeViewColumn ();
+			
+			CellRendererPixbuf pixbuf_renderer = new CellRendererPixbuf ();
+			column.PackStart (pixbuf_renderer, false);
+			column.SetCellDataFunc (pixbuf_renderer, PixbufDataFunc);
+			
+			CellRendererTextProgress folder_renderer = new CellRendererTextProgress ();
+			column.PackStart (folder_renderer, true);
+			column.SetCellDataFunc (folder_renderer, FolderDataFunc);
+			
+			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/Widgets/SaneTreeView.cs b/src/Widgets/SaneTreeView.cs
index 309b601..9ce2643 100644
--- a/src/Widgets/SaneTreeView.cs
+++ b/src/Widgets/SaneTreeView.cs
@@ -17,6 +17,11 @@ namespace FSpot.Widgets
     {
         protected bool row_selected_on_button_down, ignore_button_release, drag_started;
 
+		public SaneTreeView()
+        {
+            Selection.Mode = SelectionMode.Multiple;
+        }
+		
         public SaneTreeView(TreeStore store) : base(store)
         {
             Selection.Mode = SelectionMode.Multiple;



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