f-spot r4173 - in trunk: . src src/Core src/UI.Dialog src/Widgets



Author: sdelcroix
Date: Sun Jul 13 14:05:06 2008
New Revision: 4173
URL: http://svn.gnome.org/viewvc/f-spot?rev=4173&view=rev

Log:
2008-07-13  Stephane Delcroix  <stephane delcroix org>

	* configure.in: detect mono 1.9

	* src/Core/BrowsableEventArgs.cs:
	* src/Core/BrowsablePointer.cs:
	* src/Core/BrowsablePointerEventArgs.cs:
	* src/Core/DbItem.cs:
	* src/Core/IBrowsableCollection.cs:
	* src/Core/IBrowsableItemChanges.cs
	* src/Core/Photo.cs:
	* src/Core/PhotoChanges.cs:
	* src/Core/PhotosChanges.cs:
	* src/Db.cs:
	* src/FileImportBackend.cs:
	* src/ImportCommand.cs:
	* src/InfoOverlay.cs:
	* src/ItemAction.cs:
	* src/MainWindow.cs:
	* src/Makefile.am:
	* src/PhotoArray.cs:
	* src/PhotoEventArgs.cs:
	* src/PhotoImageView.cs:
	* src/PhotoList.cs:
	* src/PhotoQuery.cs:
	* src/PhotoStore.cs:
	* src/PhotoVersionCommands.cs:
	* src/PhotoView.cs:
	* src/QueuedSqliteDatabase.cs:
	* src/SingleView.cs:
	* src/TagStore.cs:
	* src/TimeDialog.cs:
	* src/UI.Dialog/ColorDialog.cs:
	* src/Updater.cs:
	* src/UriCollection.cs:
	* src/Widgets/Filmstrip.cs:
	* src/Widgets/IconView.cs:
	* src/Widgets/MetadataDisplay.cs:
	* src/Widgets/TagEntry.cs: Keep a list of things changed between
	Commit's in Photo, only update the things that really changed, then
	send the list of what changed along with the fired events. On normal
	usage, 2-3 versions per image, 2-3 tags per image, it reduces the
	number of db request by 90%...


Added:
   trunk/src/Core/BrowsablePointerEventArgs.cs
   trunk/src/Core/IBrowsableItemChanges.cs
   trunk/src/Core/PhotoChanges.cs
   trunk/src/Core/PhotosChanges.cs
   trunk/src/PhotoEventArgs.cs
   trunk/src/PhotoList.cs
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/src/Core/BrowsableEventArgs.cs
   trunk/src/Core/BrowsablePointer.cs
   trunk/src/Core/DbItem.cs
   trunk/src/Core/IBrowsableCollection.cs
   trunk/src/Core/Photo.cs
   trunk/src/Db.cs
   trunk/src/FileImportBackend.cs
   trunk/src/ImportCommand.cs
   trunk/src/InfoOverlay.cs
   trunk/src/ItemAction.cs
   trunk/src/MainWindow.cs
   trunk/src/Makefile.am
   trunk/src/PhotoArray.cs
   trunk/src/PhotoImageView.cs
   trunk/src/PhotoQuery.cs
   trunk/src/PhotoStore.cs
   trunk/src/PhotoVersionCommands.cs
   trunk/src/PhotoView.cs
   trunk/src/QueuedSqliteDatabase.cs
   trunk/src/SingleView.cs
   trunk/src/TagStore.cs
   trunk/src/TimeDialog.cs
   trunk/src/UI.Dialog/ColorDialog.cs
   trunk/src/Updater.cs
   trunk/src/UriCollection.cs
   trunk/src/Widgets/Filmstrip.cs
   trunk/src/Widgets/IconView.cs
   trunk/src/Widgets/MetadataDisplay.cs
   trunk/src/Widgets/TagEntry.cs

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Sun Jul 13 14:05:06 2008
@@ -123,6 +123,9 @@
 if pkg-config --atleast-version=1.2.5 mono; then
    CSC_DEFINES="$CSC_DEFINES -d:MONO_1_2_5"
 fi
+if pkg-config --atleast-version=1.9.0 mono; then
+   CSC_DEFINES="$CSC_DEFINES -d:MONO_1_9_0"
+fi
 if pkg-config --atleast-version=2.0 mono; then
    CSC_DEFINES="$CSC_DEFINES -d:MONO_2_0"
 fi

Modified: trunk/src/Core/BrowsableEventArgs.cs
==============================================================================
--- trunk/src/Core/BrowsableEventArgs.cs	(original)
+++ trunk/src/Core/BrowsableEventArgs.cs	Sun Jul 13 14:05:06 2008
@@ -17,36 +17,19 @@
 			get { return items; }
 		}
 
-		private readonly bool metadata_changed;
-		public bool MetadataChanged {
-			get { return metadata_changed; }
+		IBrowsableItemChanges changes;
+		public IBrowsableItemChanges Changes {
+			get { return changes; }
 		}
 
-		private readonly bool data_changed;
-		public bool DataChanged {
-			get { return data_changed; }
-		}
-
-		public BrowsableEventArgs (int num, bool metadata_changed, bool data_changed)
-			: this (new int [] { num }, metadata_changed, data_changed)
+		public BrowsableEventArgs (int item, IBrowsableItemChanges changes) : this (new int[] {item}, changes)
 		{
 		}
 
-		public BrowsableEventArgs (int [] items, bool metadata_changed, bool data_changed)
+		public BrowsableEventArgs (int[] items, IBrowsableItemChanges changes)
 		{
 			this.items = items;
-			this.metadata_changed = metadata_changed;
-			this.data_changed = data_changed;
-		}
-
-		[Obsolete ("You should be smarter and provide info about what changed!")]
-		public BrowsableEventArgs (int num) : this (new int [] { num }, true, true)
-		{
-		}
-
-		[Obsolete ("You should be smarter and provide info about what changed!")]
-		public BrowsableEventArgs (int [] items) : this (items, true, true)
-		{
+			this.changes = changes;
 		}
 	}
 }

Modified: trunk/src/Core/BrowsablePointer.cs
==============================================================================
--- trunk/src/Core/BrowsablePointer.cs	(original)
+++ trunk/src/Core/BrowsablePointer.cs	Sun Jul 13 14:05:06 2008
@@ -10,38 +10,6 @@
 namespace FSpot
 {
 	public delegate void ItemChangedHandler (BrowsablePointer pointer, BrowsablePointerChangedArgs old);
-
-	public class BrowsablePointerChangedArgs {
-		private readonly IBrowsableItem previous_item;
-		public IBrowsableItem PreviousItem {
-			get { return previous_item; }
-		}
-
-		private readonly int previous_index;
-		public int PreviousIndex {
-			get { return previous_index; }
-		}
-
-		private readonly bool metadata_changed;
-		public bool MetadataChanged {
-			get { return metadata_changed; }
-		}
-
-		private readonly bool data_changed;
-		public bool DataChanged {
-			get { return data_changed; }
-		}
-
-		public BrowsablePointerChangedArgs (IBrowsableItem previous_item, int previous_index,
-				bool metadata_changed, bool data_changed)
-		{
-			this.previous_item = previous_item;
-			this.previous_index = previous_index;
-			this.metadata_changed = metadata_changed;
-			this.data_changed = data_changed;
-		}
-	}
-
 	public class BrowsablePointer {
 		IBrowsableCollection collection;
 		IBrowsableItem item;
@@ -126,17 +94,21 @@
 			get { return index; }
 			set {
 				if (index != value) {
-					SetIndex (value, false, false);
+					SetIndex (value);
 				}				
 			}
 		}
 
-		private void SetIndex (int value, bool metadata_changed, bool data_changed)
+		private void SetIndex (int value)
+		{
+			SetIndex (value, null);
+		}
+
+		private void SetIndex (int value, IBrowsableItemChanges changes)
 		{
 			BrowsablePointerChangedArgs args;
 			
-			args = new BrowsablePointerChangedArgs (Current, index,
-					metadata_changed, data_changed);
+			args = new BrowsablePointerChangedArgs (Current, index, changes);
 			
 			index = value;
 			item = Current;
@@ -150,7 +122,7 @@
 		{
 			foreach (int item in event_args.Items)
 				if (item == Index) 
-					SetIndex (Index, event_args.MetadataChanged, event_args.DataChanged);
+					SetIndex (Index, event_args.Changes);
 		}
 		
 		protected void HandleCollectionChanged (IBrowsableCollection collection)
@@ -160,17 +132,17 @@
 			
 			if (old_location == next_location) {
 				if (! Valid (next_location))
-					SetIndex (0, false, false);
+					SetIndex (0, null);
 
 				return;
 			}
 			
 			if (Valid (next_location))
-				SetIndex (next_location, false, false);
+				SetIndex (next_location);
 			else if (Valid (old_location))
-				SetIndex (old_location, false, false);
+				SetIndex (old_location);
 			else
-				SetIndex (0, false, false);
+				SetIndex (0);
 		}
 	}
 }

Added: trunk/src/Core/BrowsablePointerEventArgs.cs
==============================================================================
--- (empty file)
+++ trunk/src/Core/BrowsablePointerEventArgs.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,35 @@
+/*
+ * FSpot.BrowsablePointerEventArgs.cs
+ *
+ * Author(s):
+ *	Larry Ewing <lewing novell com>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+namespace FSpot
+{
+	public class BrowsablePointerChangedArgs {
+		IBrowsableItem previous_item;
+		public IBrowsableItem PreviousItem {
+			get { return previous_item; }
+		}
+
+		int previous_index;
+		public int PreviousIndex {
+			get { return previous_index; }
+		}
+
+		IBrowsableItemChanges changes;
+		public IBrowsableItemChanges Changes {
+			get { return changes; }
+		}
+
+		public BrowsablePointerChangedArgs (IBrowsableItem previous_item, int previous_index, IBrowsableItemChanges changes)
+		{
+			this.previous_item = previous_item;
+			this.previous_index = previous_index;
+			this.changes = changes;
+		}
+	}
+}

Modified: trunk/src/Core/DbItem.cs
==============================================================================
--- trunk/src/Core/DbItem.cs	(original)
+++ trunk/src/Core/DbItem.cs	Sun Jul 13 14:05:06 2008
@@ -3,6 +3,7 @@
  *
  * Author(s):
  *	Larry Ewing
+ *	Stephane Delcroix
  *
  * This is free software. See COPYING for details.
  */
@@ -19,4 +20,22 @@
 			this.id = id;
 		}
 	}
+
+	public class DbItemEventArgs {
+		private DbItem [] items;
+
+		public DbItem [] Items {
+			get { return items; }
+		}
+
+		public DbItemEventArgs (DbItem [] items)
+		{
+			this.items = items;
+		}
+
+		public DbItemEventArgs (DbItem item)
+		{
+			this.items = new DbItem [] { item };
+		}
+	}
 }

Modified: trunk/src/Core/IBrowsableCollection.cs
==============================================================================
--- trunk/src/Core/IBrowsableCollection.cs	(original)
+++ trunk/src/Core/IBrowsableCollection.cs	Sun Jul 13 14:05:06 2008
@@ -38,7 +38,7 @@
 		event IBrowsableCollectionChangedHandler Changed;
 		event IBrowsableCollectionItemsChangedHandler ItemsChanged;
 
-		void MarkChanged (int index);
+		void MarkChanged (int index, IBrowsableItemChanges changes);
 	}
 }
 

Added: trunk/src/Core/IBrowsableItemChanges.cs
==============================================================================
--- (empty file)
+++ trunk/src/Core/IBrowsableItemChanges.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,28 @@
+/*
+ * FSpot.IBrowsableItemChanges.cs
+ *
+ * Author(s):
+ * 	Stephane Delcroix <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details
+ */
+
+namespace FSpot
+{
+	public interface IBrowsableItemChanges
+	{
+		bool DataChanged {get;}
+		bool MetadataChanged {get;}
+	}
+
+	public class FullInvalidate : IBrowsableItemChanges
+	{
+		public static FullInvalidate Instance = new FullInvalidate ();
+		public bool DataChanged {
+			get { return true; }
+		}
+		public bool MetadataChanged {
+			get { return true; }
+		}
+	}
+}

Modified: trunk/src/Core/Photo.cs
==============================================================================
--- trunk/src/Core/Photo.cs	(original)
+++ trunk/src/Core/Photo.cs	Sun Jul 13 14:05:06 2008
@@ -74,9 +74,9 @@
 			return string.Compare (photo1.Name, photo2.Name);
 		}
 	
-		public class CompareDateName : IComparer
+		public class CompareDateName : IComparer<IBrowsableItem>
 		{
-			public int Compare (object obj1, object obj2)
+			public int Compare (IBrowsableItem obj1, IBrowsableItem obj2)
 			{
 				Photo p1 = (Photo)obj1;
 				Photo p2 = (Photo)obj2;
@@ -116,11 +116,26 @@
 			}
 		}
 	
+		PhotoChanges changes = new PhotoChanges ();
+		internal PhotoChanges Changes {
+			get{ return changes; }
+			set {
+				if (value != null)
+					throw new ArgumentException ("The only valid value is null");
+				changes = new PhotoChanges ();
+			}
+		}
+
 		// The time is always in UTC.
 		private DateTime time;
 		public DateTime Time {
 			get { return time; }
-			set { time = value; }
+			set {
+				if (time == value)
+					return;
+				time = value;
+				changes.TimeChanged = true;
+			}
 		}
 	
 		public string Name {
@@ -157,21 +172,33 @@
 		private string description;
 		public string Description {
 			get { return description; }
-			set { description = value; }
+			set {
+				if (description == value)
+					return;
+				description = value;
+				changes.DescriptionChanged = true;
+			}
 		}
 	
 		private uint roll_id = 0;
 		public uint RollId {
 			get { return roll_id; }
-			set { roll_id = value; }
+			set {
+				if (roll_id == value)
+					return;
+				roll_id = value;
+				changes.RollIdChanged = true;
+			}
 		}
 	
 		private uint rating;
 		public uint Rating {
 			get { return rating; }
 			set {
-				if (value >= 0 && value <= 5)
-					rating = value;
+				if (rating == value || value < 0 || value > 5)
+					return;
+				rating = value;
+				changes.RatingChanged = true;
 			}
 		}
 	
@@ -211,7 +238,12 @@
 		private uint default_version_id = OriginalVersionId;
 		public uint DefaultVersionId {
 			get { return default_version_id; }
-			set { default_version_id = value; }
+			set {
+				if (default_version_id == value)
+					return;
+				default_version_id = value;
+				changes.DefaultVersionIdChanged = true;
+			}
 		}
 	
 		// This doesn't check if a version of that name already exists, 
@@ -221,6 +253,7 @@
 			Versions [version_id] = new PhotoVersion (this, version_id, uri, name, is_protected);
 	
 			highest_version_id = Math.Max (version_id, highest_version_id);
+			changes.AddVersion (version_id);
 		}
 	
 		public uint AddVersion (System.Uri uri, string name)
@@ -234,6 +267,8 @@
 				throw new ApplicationException ("A version with that name already exists");
 			highest_version_id ++;
 			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, uri, name, is_protected);
+
+			changes.AddVersion (highest_version_id);
 			return highest_version_id;
 		}
 	
@@ -350,7 +385,9 @@
 				PhotoStore.DeleteThumbnail (uri);
 			}
 			Versions.Remove (version_id);
-	
+
+			changes.RemoveVersion (version_id);
+
 			do {
 				version_id --;
 				if (Versions.ContainsKey (version_id)) {
@@ -400,6 +437,8 @@
 			}
 			highest_version_id ++;
 			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, new_uri, name, is_protected);
+
+			changes.AddVersion (highest_version_id);
 	
 			return highest_version_id;
 		}
@@ -422,7 +461,9 @@
 	
 				highest_version_id ++;
 				Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, version.Uri, name, is_protected);
-	
+
+				changes.AddVersion (highest_version_id);
+
 				return highest_version_id;
 			}
 		}
@@ -473,6 +514,8 @@
 				throw new Exception ("This name already exists");
 	
 			(GetVersion (version_id) as PhotoVersion).Name = new_name;
+
+			changes.ChangeVersion (version_id);
 	
 			//TODO: rename file too ???
 	
@@ -493,6 +536,7 @@
 				tags = new ArrayList ();
 	
 			tags.Add (tag);
+			changes.AddTag (tag);
 		}
 	
 		// This on the other hand does, but is O(n) with n being the number of existing tags.
@@ -514,8 +558,11 @@
 	
 		public void RemoveTag (Tag tag)
 		{
-			if (HasTag (tag))
-				tags.Remove (tag);
+			if (!HasTag (tag))
+				return;
+
+			tags.Remove (tag);
+			changes.RemoveTag (tag);
 		}
 	
 		public void RemoveTag (Tag []taglist)

Added: trunk/src/Core/PhotoChanges.cs
==============================================================================
--- (empty file)
+++ trunk/src/Core/PhotoChanges.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,156 @@
+/*
+ * FSpot.PhotoChanges.cs
+ *
+ * Author(s):
+ *	Stephane Delcroix  <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace FSpot
+{
+	//Track the changes done to a Photo between Commit's
+	public class PhotoChanges : PhotosChanges
+	{
+
+		public override bool VersionsChanged {
+			get { return VersionsAdded == null && VersionsRemoved == null && VersionsModified == null; }
+		}
+
+		public bool TagsChanged {
+			get { return TagsAdded == null && TagsRemoved == null; }
+		}
+
+		List<Tag> tags_added = null;
+		public Tag [] TagsAdded {
+			get {
+				if (tags_added == null)
+					return null;
+				if (tags_added.Count == 0)
+					return null;
+				return tags_added.ToArray ();
+			}
+			set {
+				foreach (Tag t in value)
+					AddTag (t);
+			}
+		}
+
+		public void AddTag (Tag t)
+		{
+			if (tags_added == null)
+				tags_added = new List<Tag> ();
+			if (tags_removed != null)
+				tags_removed.Remove (t);
+			tags_added.Add (t);
+
+		}
+
+		List<Tag> tags_removed = null;
+		public Tag [] TagsRemoved {
+			get {
+				if (tags_removed == null)
+					return null;
+				if (tags_removed.Count == 0)
+					return null;
+				return tags_removed.ToArray ();
+			}
+			set {
+				foreach (Tag t in value)
+					RemoveTag (t);
+			}
+		}
+
+		public void RemoveTag (Tag t)
+		{
+			if (tags_removed == null)
+				tags_removed = new List<Tag> ();
+			if (tags_added != null)
+				tags_added.Remove (t);
+			tags_removed.Add (t);
+		}
+
+
+		List<uint> versions_added = null;
+		public uint [] VersionsAdded {
+			get {
+				if (versions_added != null)
+					return null;
+				if (versions_added.Count == 0)
+					return null;
+				return versions_added.ToArray ();
+			}
+			set {
+				foreach (uint u in value)
+					AddVersion (u);
+			}
+		}
+
+		public void AddVersion (uint v)
+		{
+			if (versions_added == null)
+				versions_added = new List<uint> ();
+			versions_added.Add (v);
+		}
+
+		List<uint> versions_removed = null;
+		public uint [] VersionsRemoved {
+			get {
+				if (versions_removed == null)
+					return null;
+				if (versions_removed.Count == 0)
+					return null;
+				return versions_removed.ToArray ();
+			}
+			set {
+				foreach (uint u in value)
+					RemoveVersion (u);
+			}
+		}
+
+		public void RemoveVersion (uint v)
+		{
+			if (versions_removed == null)
+				versions_removed= new List<uint> ();
+			if (versions_added != null)
+				versions_added.Remove (v);
+			if (versions_modified != null)
+				versions_modified.Remove (v);
+			versions_removed.Add (v);
+		}
+
+
+		List<uint> versions_modified = null;
+		public uint [] VersionsModified {
+			get {
+				if (versions_modified == null)
+					return null;
+				if (versions_modified.Count == 0)
+					return null;
+				return versions_modified.ToArray ();
+			}
+			set {
+				foreach (uint u in value)
+					ChangeVersion (u);
+			}
+		}
+
+		public void ChangeVersion (uint v)
+		{
+			if (versions_modified == null)
+				versions_modified = new List<uint> ();
+			if (versions_added.Contains (v))
+				return;
+			if (versions_removed.Contains (v))
+				return;
+			versions_modified.Add (v);
+		}
+
+		public PhotoChanges ()
+		{
+		}
+	}
+}

Added: trunk/src/Core/PhotosChanges.cs
==============================================================================
--- (empty file)
+++ trunk/src/Core/PhotosChanges.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,124 @@
+/*
+ * FSpot.PhotosChanges.cs
+ *
+ * Author(s):
+ *	Stephane Delcroix  <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace FSpot
+{
+	//used to aggregate PhotoChanges and notifying the various ui pieces
+	public class PhotosChanges : IBrowsableItemChanges
+	{
+		[Flags ()]
+		enum Changes {
+			None 			= 0x0,
+			DefaultVersionId 	= 0x1,
+			Time			= 0x2,
+			Uri			= 0x4,
+			Rating			= 0x8,
+			Description		= 0x10,
+			RollId			= 0x20,
+			Data			= 0x40,
+		}
+
+		Changes changes = Changes.None;
+
+		public bool MetadataChanged {
+			get { return (changes & ~Changes.Data) != Changes.None || TagsChanged || VersionsChanged; }
+		}
+
+		public bool DataChanged {
+			get { return (changes & Changes.Data) == Changes.Data; }
+			set {
+				if (value)
+					changes |= Changes.Data;
+				else
+					changes &= ~Changes.Data;
+			}
+		}
+		public bool DefaultVersionIdChanged {
+			get { return (changes & Changes.DefaultVersionId) == Changes.DefaultVersionId; }
+			set {
+				if (value) {
+					changes |= Changes.DefaultVersionId;
+					DataChanged = true;
+				} else
+					changes &= ~Changes.DefaultVersionId;
+			}
+		}
+		public bool TimeChanged {
+			get { return (changes & Changes.Time) == Changes.Time; }
+			set {
+				if (value)
+					changes |= Changes.Time;
+				else
+					changes &= ~Changes.Time;
+			}
+		}
+		public bool UriChanged {
+			get { return (changes & Changes.Uri) == Changes.Uri; }
+			set {
+				if (value)
+					changes |= Changes.Uri;
+				else
+					changes &= ~Changes.Uri;
+			}
+		}
+		public bool RatingChanged {
+			get { return (changes & Changes.Rating) == Changes.Rating; }
+			set {
+				if (value)
+					changes |= Changes.Rating;
+				else
+					changes &= ~Changes.Rating;
+			}
+		}
+		public bool DescriptionChanged {
+			get { return (changes & Changes.Description) == Changes.Description; }
+			set {
+				if (value)
+					changes |= Changes.Description;
+				else
+					changes &= ~ Changes.Description;
+			}
+		}
+		bool tags_changed = false;
+		public bool TagsChanged {
+			get { return tags_changed; }
+			private set { tags_changed = value; }
+		}
+		bool versions_changed = false;
+		public virtual bool VersionsChanged {
+			get { return versions_changed; }
+			private set { versions_changed = value; }
+		}
+		public bool RollIdChanged {
+			get { return (changes & Changes.RollId) == Changes.RollId; }
+			set {
+				if (value)
+					changes |= Changes.RollId;
+				else
+					changes &= ~Changes.RollId;
+			}
+		}
+
+		public static PhotosChanges operator | (PhotosChanges c1, PhotosChanges c2)
+		{
+			PhotosChanges changes = new PhotosChanges ();
+			changes.changes = c1.changes | c2.changes;
+			changes.VersionsChanged = c1.VersionsChanged || c2.VersionsChanged;
+			changes.TagsChanged = c1.TagsChanged || c2.TagsChanged;
+			return changes;
+		}
+
+		public PhotosChanges ()
+		{
+		}
+	}
+}

Modified: trunk/src/Db.cs
==============================================================================
--- trunk/src/Db.cs	(original)
+++ trunk/src/Db.cs	Sun Jul 13 14:05:06 2008
@@ -7,24 +7,6 @@
 using FSpot;
 using FSpot.Utils;
 
-public class DbItemEventArgs {
-	private DbItem [] items;
-	
-	public DbItem [] Items {
-		get { return items; }
-	}
-	
-	public DbItemEventArgs (DbItem [] items)
-	{
-		this.items = items;
-	}
-	
-	public DbItemEventArgs (DbItem item)
-	{
-		this.items = new DbItem [] { item };
-	}
-}
-
 // A Store maps to a SQL table.  We have separate stores (i.e. SQL tables) for tags, photos and imports.
 
 public delegate void ItemsAddedHandler (object sender, DbItemEventArgs args);

Modified: trunk/src/FileImportBackend.cs
==============================================================================
--- trunk/src/FileImportBackend.cs	(original)
+++ trunk/src/FileImportBackend.cs	Sun Jul 13 14:05:06 2008
@@ -244,7 +244,7 @@
 			needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
 
 			if (needs_commit)
-				store.Commit(photo, true, true);
+				store.Commit(photo);
 			
 			info.Photo = photo;
 		} catch (System.Exception e) {

Modified: trunk/src/ImportCommand.cs
==============================================================================
--- trunk/src/ImportCommand.cs	(original)
+++ trunk/src/ImportCommand.cs	Sun Jul 13 14:05:06 2008
@@ -689,7 +689,7 @@
 						continue;
 					
 					p.AddTag ((Tag [])tags_selected.ToArray(typeof(Tag)));
-					store.Commit (p, true, true);
+					store.Commit (p);
 				}
 			}
 

Modified: trunk/src/InfoOverlay.cs
==============================================================================
--- trunk/src/InfoOverlay.cs	(original)
+++ trunk/src/InfoOverlay.cs	Sun Jul 13 14:05:06 2008
@@ -35,7 +35,7 @@
 
 			if (p !=  null && q != null) {
 				p.DefaultVersionId  = version_id;
-				q.Commit (item.Index, true, false);
+				q.Commit (item.Index);
 			}
 		}
 	}

Modified: trunk/src/ItemAction.cs
==============================================================================
--- trunk/src/ItemAction.cs	(original)
+++ trunk/src/ItemAction.cs	Sun Jul 13 14:05:06 2008
@@ -62,7 +62,7 @@
 				
 				while (op.Step ());
 				
-				item.Collection.MarkChanged (item.Index);
+				item.Collection.MarkChanged (item.Index, FullInvalidate.Instance);
 			} catch (Exception e) {
 				Dialog d = new EditExceptionDialog (null, e, item.Current);
 				d.Show ();
@@ -179,9 +179,10 @@
 			PhotoQuery q = item.Collection as PhotoQuery;
 			if (photo != null && q != null) {
 				photo.DefaultVersionId = version;
-				q.Commit (item.Index, true, true);
+				photo.Changes.DataChanged = true;
+				q.Commit (item.Index);
 			} else {
-				item.Collection.MarkChanged (item.Index);
+				item.Collection.MarkChanged (item.Index, FullInvalidate.Instance);
 			}
 		}
 		

Modified: trunk/src/MainWindow.cs
==============================================================================
--- trunk/src/MainWindow.cs	(original)
+++ trunk/src/MainWindow.cs	Sun Jul 13 14:05:06 2008
@@ -7,6 +7,7 @@
 using System.Text;
 
 using System.Collections;
+using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Text.RegularExpressions;
 using System.Web;
@@ -588,7 +589,7 @@
 				FSpot.Jobs.SyncMetadataJob.Create (db.Jobs, p);
 		}
 		
-		if (args is TimeChangedEventArgs)
+		if (args is PhotoEventArgs && (args as PhotoEventArgs).Changes.TimeChanged)
 			query.RequestReload ();
 	}
 
@@ -683,7 +684,7 @@
 				Changed (this);
 		}
 
-		public void MarkChanged (int index)
+		public void MarkChanged (int index, IBrowsableItemChanges changes)
 		{
 			throw new System.NotImplementedException ("I didn't think you'd find me");
 		}
@@ -727,7 +728,7 @@
 
 			foreach (int item in args.Items) {
 				if (win.photo_view.Item.Index == item ) {
-					ItemsChanged (this, new BrowsableEventArgs (0));
+					ItemsChanged (this, new BrowsableEventArgs (item, args.Changes));
 					break;
 				}
 			}
@@ -826,7 +827,15 @@
 		
 		int [] selected_ids = SelectedIds ();
 		if (command.Execute (direction, SelectedPhotos (selected_ids)))
-			query.MarkChanged (selected_ids, true, true);
+#if MONO_1_9_0
+			query.MarkChanged (selected_ids, new PhotoChanges () {DataChanged = true});
+#else
+		{
+			PhotoChanges changes = new PhotoChanges ();
+			changes.DataChanged = true;
+			query.MarkChanged (selected_ids, changes);
+		}
+#endif
 	}
 
 	//
@@ -842,7 +851,7 @@
 	{
 		foreach (int num in nums)
 			query.Photos [num].AddTag (tags);
-		query.Commit (nums, true, false);
+		query.Commit (nums);
 
 		foreach (Tag t in tags) {
 			if (t.Icon != null)
@@ -866,7 +875,7 @@
 	{
 		foreach (int num in nums)
 			query.Photos [num].RemoveTag (tags);
-		query.Commit (nums, true, false);
+		query.Commit (nums);
 	}
 
 	void HandleTagSelectionRowActivated (object sender, RowActivatedArgs args)
@@ -993,6 +1002,7 @@
 			UriList list = new UriList (args.SelectionData);
 			
 			db.BeginTransaction ();
+			List<Photo> photos = new List<Photo> ();
 			foreach (string photo_path in list.ToLocalPaths ()) {
 				Photo photo = db.Photos.GetByPath (photo_path);
 				
@@ -1002,8 +1012,9 @@
 				
 				// FIXME this should really follow the AddTagsExtended path too
 				photo.AddTag (new Tag[] {tag});
-				db.Photos.Commit (photo, true, false);
+				photos.Add (photo);
 			}
+			db.Photos.Commit (photos.ToArray ());
 			db.CommitTransaction ();
 			InvalidateViews ();
 			break;
@@ -1498,7 +1509,7 @@
 			p = query.Photos [num];
 			p.Rating = (uint) r;
 		}
-		query.Commit (selected_photos, true, false);
+		query.Commit (selected_photos);
 		db.CommitTransaction ();
 	}
 
@@ -1848,18 +1859,20 @@
 	{
 		PhotoVersionCommands.Create cmd = new PhotoVersionCommands.Create ();
 
-		if (cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null))) {
-			query.MarkChanged (ActiveIndex (), true, false);
-		}
+		cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null));
+//		if (cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null))) {
+//			query.MarkChanged (ActiveIndex (), true, false);
+//		}
 	}
 
 	void HandleDeleteVersionCommand (object obj, EventArgs args)
 	{
 		PhotoVersionCommands.Delete cmd = new PhotoVersionCommands.Delete ();
 
-		if (cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null))) {
-			query.MarkChanged (ActiveIndex (), true, true);
-		}
+		cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null));
+//		if (cmd.Execute (db.Photos, CurrentPhoto, GetToplevel (null))) {
+//			query.MarkChanged (ActiveIndex (), true, true);
+//		}
 	}
 
 	void HandlePropertiesCommand (object obje, EventArgs args)
@@ -1881,9 +1894,10 @@
 	{
 		PhotoVersionCommands.Rename cmd = new PhotoVersionCommands.Rename ();
 
-		if (cmd.Execute (db.Photos, CurrentPhoto, main_window)) {
-			query.MarkChanged (ActiveIndex (), true, false);
-		}
+		cmd.Execute (db.Photos, CurrentPhoto, main_window);
+//		if (cmd.Execute (db.Photos, CurrentPhoto, main_window)) {
+//			query.MarkChanged (ActiveIndex (), true, false);
+//		}
 	}
 	
 	public void HandleCreateTagAndAttach (object sender, EventArgs args)
@@ -2456,15 +2470,21 @@
 		}
 	}
 
-	void HandleUpdateThumbnailCommand (object sende, EventArgs args)
+	void HandleUpdateThumbnailCommand (object sender, EventArgs args)
 	{
 		ThumbnailCommand command = new ThumbnailCommand (main_window);
 
 		int [] selected_ids = SelectedIds ();
-		if (command.Execute (SelectedPhotos (selected_ids))) {
-			foreach (int num in selected_ids)
-				query.MarkChanged (num, false, true);
+		if (command.Execute (SelectedPhotos (selected_ids)))
+#if MONO_1_9_0
+			query.MarkChanged (selected_ids, new PhotoChanges {DataChanged = true});
+#else
+		{
+			PhotoChanges changes = new PhotoChanges ();
+			changes.DataChanged = true;
+			query.MarkChanged (selected_ids, changes);
 		}
+#endif
 	}
 
 	public void HandleRotate90Command (object sender, EventArgs args)
@@ -2689,7 +2709,7 @@
 	void UpdateForVersionIdChange (uint version_id)
 	{
 		CurrentPhoto.DefaultVersionId = version_id;
-		query.Commit (ActiveIndex (), true, true);
+		query.Commit (ActiveIndex ());
 	}
 
 	// Queries.
@@ -2974,7 +2994,6 @@
 					uint version = photo.CreateNamedVersion (mime_application.Name, photo.DefaultVersionId, true);
 					photo.DefaultVersionId = version;
 				}
-				query.MarkChanged (query.IndexOf (photo), true, true);
 			} catch (Exception e) {
 				errors.Add (new EditException (photo, e));
 			}
@@ -2989,9 +3008,8 @@
 			md.Destroy ();
 		}
 
-		if (create_new_versions) {
-			db.Photos.Commit (selected, true, false);
-		}
+		if (create_new_versions)
+			db.Photos.Commit (selected);
 
 		mime_application.Launch (uri_list);
 	}

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Sun Jul 13 14:05:06 2008
@@ -21,14 +21,18 @@
 	$(srcdir)/Core/Animator.cs		\
 	$(srcdir)/Core/BrowsableEventArgs.cs	\
 	$(srcdir)/Core/BrowsablePointer.cs	\
+	$(srcdir)/Core/BrowsablePointerEventArgs.cs	\
 	$(srcdir)/Core/Category.cs		\
 	$(srcdir)/Core/DbItem.cs		\
 	$(srcdir)/Core/Delay.cs			\
 	$(srcdir)/Core/Tag.cs			\
 	$(srcdir)/Core/Global.cs		\
 	$(srcdir)/Core/IBrowsableItem.cs	\
+	$(srcdir)/Core/IBrowsableItemChanges.cs	\
 	$(srcdir)/Core/IBrowsableCollection.cs	\
 	$(srcdir)/Core/IPreferenceBackend.cs	\
+	$(srcdir)/Core/PhotoChanges.cs		\
+	$(srcdir)/Core/PhotosChanges.cs		\
 	$(srcdir)/Core/Roll.cs
 
 QUERY_CSDISTFILES =				\
@@ -77,6 +81,7 @@
 	$(srcdir)/BlockProcessor.cs		\
 	$(srcdir)/BitConverter.cs		\
 	$(srcdir)/PhotoArray.cs 		\
+	$(srcdir)/PhotoList.cs 		\
 	$(srcdir)/ColorAdjustment.cs		\
 	$(srcdir)/CompatFileChooser.cs		\
 	$(srcdir)/ControlOverlay.cs		\
@@ -165,6 +170,7 @@
 	$(srcdir)/MetadataStore.cs		\
 	$(srcdir)/NullPreferenceBackend.cs	\
 	$(srcdir)/Operation.cs			\
+	$(srcdir)/PhotoEventArgs.cs		\
 	$(srcdir)/PhotoImageView.cs		\
 	$(srcdir)/PhotoLoader.cs		\
 	$(srcdir)/PhotoPopup.cs			\

Modified: trunk/src/PhotoArray.cs
==============================================================================
--- trunk/src/PhotoArray.cs	(original)
+++ trunk/src/PhotoArray.cs	Sun Jul 13 14:05:06 2008
@@ -1,111 +1,14 @@
-using System.Collections;
-
-namespace FSpot {
-	public class PhotoList : IBrowsableCollection {
-		protected System.Collections.ArrayList list;
-		IBrowsableItem [] cache;
-
-		public PhotoList (IBrowsableItem [] photos)
-		{
-			list = new System.Collections.ArrayList (photos);
-		}
-
-		public PhotoList () 
-		{
-			list = new System.Collections.ArrayList ();
-		}
-
-		public int Count {
-			get {
-				return list.Count;
-			}
-		}
-
-		public void Clear ()
-		{
-			list.Clear ();
-			Reload ();
-		}
-
-		public int Capacity {
-			set {
-				list.Capacity = value;
-			}
-		}
-
-		public void Add (IBrowsableItem photo)
-		{
-			list.Add (photo);
-			Reload ();
-		}
-
-		public void Add (IBrowsableItem [] items)
-		{
-			list.AddRange (items);
-			Reload ();
-		}
-		
-		public int IndexOf (IBrowsableItem item)
-		{
-			return list.IndexOf (item);
-		}
-		
-		public bool Contains (IBrowsableItem item)
-		{
-			return list.Contains (item);
-		}
-
-		public IBrowsableItem this [int index] {
-			get {
-				return (IBrowsableItem) list [index];
-			}
-			set {
-				list [index] = value;
-				MarkChanged (index);
-			}
-		}
-		
-		public void Sort (IComparer compare)
-		{
-			list.Sort (compare);
-			Reload ();
-		}
-		
-		public void Reload ()
-		{
-			cache = null;
-			if (Changed != null)
-				Changed (this);
-		}
-		
-		public void MarkChanged (int num)
-		{
-			MarkChanged (new BrowsableEventArgs (num));
-		}
-
-		public void MarkChanged (BrowsableEventArgs args)
-		{
-			if (ItemsChanged != null)
-				ItemsChanged (this, args);
-		}
-
-		public IBrowsableItem [] Items {
-			get {
-				if (cache == null)
-					cache = (IBrowsableItem []) list.ToArray (typeof (IBrowsableItem));
-
-				return cache;
-			}
-			set {
-				list.Clear ();
-				Add (value);
-			}
-		}
-
-		public event IBrowsableCollectionChangedHandler Changed;
-		public event IBrowsableCollectionItemsChangedHandler ItemsChanged;
-	}
+/*
+ * FSpot.PhotoArray.cs
+ *
+ * Author(s):
+ *	Larry Ewing
+ *
+ * This is free software, See COPYING for details
+ */
 
+namespace FSpot
+{
 	public class PhotoArray : IBrowsableCollection {
 		IBrowsableItem [] photos;
 
@@ -115,9 +18,7 @@
 		}
 
 		public int Count {
-			get {
-				return photos.Length;
-			}
+			get { return photos.Length; }
 		}
 
 		/*
@@ -135,15 +36,11 @@
 
 		// IBrowsableCollection
 		public IBrowsableItem [] Items {
-			get {
-				return photos;
-			}
+			get { return photos; }
 		}
 
 		public IBrowsableItem this [int index] {
-			get {
-				return photos [index];
-			}
+			get { return photos [index]; }
 		}
 		
 		public int IndexOf (IBrowsableItem item)
@@ -156,10 +53,10 @@
 			return IndexOf (item) >= 0;
 		}
 
-		public void MarkChanged (int item)
+		public void MarkChanged (int item, IBrowsableItemChanges changes)
 		{
 			if (ItemsChanged != null)
-				ItemsChanged (this, new BrowsableEventArgs (item));
+				ItemsChanged (this, new BrowsableEventArgs (item, changes));
 		}
 
 		public void Reload ()

Added: trunk/src/PhotoEventArgs.cs
==============================================================================
--- (empty file)
+++ trunk/src/PhotoEventArgs.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,28 @@
+/*
+ * FSpot.PhotoEventArgs.cs
+ *
+ * Author(s):
+ *	Ruben Vermeersch
+ *	Stephane Delcroix  <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+namespace FSpot
+{
+	public class PhotoEventArgs : DbItemEventArgs {
+		PhotosChanges changes;
+		public PhotosChanges Changes {
+			get { return changes; }
+		}
+
+		public PhotoEventArgs (Photo photo, PhotosChanges changes) : this (new Photo[] {photo}, changes)
+		{
+		}
+
+		public PhotoEventArgs (Photo[] photos, PhotosChanges changes) : base (photos)
+		{
+			this.changes = changes;
+		}
+	}
+}

Modified: trunk/src/PhotoImageView.cs
==============================================================================
--- trunk/src/PhotoImageView.cs	(original)
+++ trunk/src/PhotoImageView.cs	Sun Jul 13 14:05:06 2008
@@ -315,8 +315,8 @@
 				return;
 
 			// Don't reload if the image didn't change at all.
-			if (!args.DataChanged &&
-			    args != null &&
+			if (args != null && args.Changes != null &&
+			    !args.Changes.DataChanged &&
 			    args.PreviousItem != null &&
 			    Item.IsValid &&
 			    this.Item.Current.DefaultVersionUri == args.PreviousItem.DefaultVersionUri)

Added: trunk/src/PhotoList.cs
==============================================================================
--- (empty file)
+++ trunk/src/PhotoList.cs	Sun Jul 13 14:05:06 2008
@@ -0,0 +1,111 @@
+/*
+ * FSpot.PhotoList.cs
+ *
+ * Author(s):
+ *	Larry Ewing
+ *
+ * This is free software, See COPYING for details
+ */
+
+using System.Collections.Generic;
+
+namespace FSpot {
+	public class PhotoList : IBrowsableCollection {
+		protected List<IBrowsableItem> list;
+		IBrowsableItem [] cache;
+
+		public PhotoList (IBrowsableItem [] photos)
+		{
+			list = new List<IBrowsableItem> (photos);
+		}
+
+		public PhotoList ()
+		{
+			list = new List<IBrowsableItem> ();
+		}
+
+		public int Count {
+			get { return list.Count; }
+		}
+
+		public void Clear ()
+		{
+			list.Clear ();
+			Reload ();
+		}
+
+		public int Capacity {
+			set { list.Capacity = value; }
+		}
+
+		public void Add (IBrowsableItem photo)
+		{
+			list.Add (photo);
+			Reload ();
+		}
+
+		public void Add (IBrowsableItem [] items)
+		{
+			list.AddRange (items);
+			Reload ();
+		}
+
+		public int IndexOf (IBrowsableItem item)
+		{
+			return list.IndexOf (item);
+		}
+
+		public bool Contains (IBrowsableItem item)
+		{
+			return list.Contains (item);
+		}
+
+		public IBrowsableItem this [int index] {
+			get { return list [index]; }
+			set {
+				list [index] = value;
+				MarkChanged (index, FullInvalidate.Instance);
+			}
+		}
+
+		public void Sort (IComparer<IBrowsableItem> compare)
+		{
+			list.Sort (compare);
+			Reload ();
+		}
+
+		public void Reload ()
+		{
+			cache = null;
+			if (Changed != null)
+				Changed (this);
+		}
+
+		public void MarkChanged (int num, IBrowsableItemChanges changes)
+		{
+			MarkChanged (new BrowsableEventArgs (num, changes));
+		}
+
+		public void MarkChanged (BrowsableEventArgs args)
+		{
+			if (ItemsChanged != null)
+				ItemsChanged (this, args);
+		}
+
+		public IBrowsableItem [] Items {
+			get {
+				if (cache == null)
+					cache = list.ToArray ();
+
+				return cache;
+			}
+			set {
+				list.Clear ();
+				Add (value);
+			}
+		}
+
+		public event IBrowsableCollectionChangedHandler Changed;
+		public event IBrowsableCollectionItemsChangedHandler ItemsChanged;
+	}
+}

Modified: trunk/src/PhotoQuery.cs
==============================================================================
--- trunk/src/PhotoQuery.cs	(original)
+++ trunk/src/PhotoQuery.cs	Sun Jul 13 14:05:06 2008
@@ -185,17 +185,17 @@
 			return System.Array.IndexOf (photos, photo);
 		}
 		
-		public void Commit (int index, bool metadata_changed, bool data_changed)
+		public void Commit (int index)
 		{
-			Commit (new int [] {index}, metadata_changed, data_changed);
+			Commit (new int [] {index});
 		}
 
-		public void Commit (int [] indexes, bool metadata_changed, bool data_changed)
+		public void Commit (int [] indexes)
 		{
 			List<Photo> to_commit = new List<Photo>();
 			foreach (int index in indexes)
 				to_commit.Add (photos [index]);
-			store.Commit (to_commit.ToArray (), metadata_changed, data_changed);
+			store.Commit (to_commit.ToArray ());
 		}
 
 		private void MarkChanged (object sender, DbItemEventArgs args)
@@ -210,33 +210,18 @@
 					indexes.Add (index);
 			}
 
-			PhotoEventArgs photo_args = args as PhotoEventArgs;
-
 			if (indexes.Count > 0 && ItemsChanged != null)
-				ItemsChanged (this, new BrowsableEventArgs(indexes.ToArray (),
-							photo_args.MetadataChanged, photo_args.DataChanged));
-		}
-
-		public void MarkChanged (int index, bool metadata_changed, bool data_changed)
-		{
-			MarkChanged (new int [] {index}, metadata_changed, data_changed);
-		}
-
-		public void MarkChanged (int [] indexes, bool metadata_changed, bool data_changed)
-		{
-			ItemsChanged (this, new BrowsableEventArgs(indexes, metadata_changed, data_changed));
+				ItemsChanged (this, new BrowsableEventArgs(indexes.ToArray (), (args as PhotoEventArgs).Changes));
 		}
 
-		[Obsolete ("You should provide info on what changed!")]
-		public void MarkChanged (int index)
+		public void MarkChanged (int index, IBrowsableItemChanges changes)
 		{
-			MarkChanged (new int [] {index});
+			MarkChanged (new int [] {index}, changes);
 		}
 
-		[Obsolete ("You should provide info on what changed!")]
-		private void MarkChanged (params int [] indexes)
+		public void MarkChanged (int [] indexes, IBrowsableItemChanges changes)
 		{
-			ItemsChanged (this, new BrowsableEventArgs (indexes));
+			ItemsChanged (this, new BrowsableEventArgs (indexes, changes));
 		}
 	}
 }

Modified: trunk/src/PhotoStore.cs
==============================================================================
--- trunk/src/PhotoStore.cs	(original)
+++ trunk/src/PhotoStore.cs	Sun Jul 13 14:05:06 2008
@@ -29,29 +29,6 @@
 
 using Banshee.Database;
 
-public class PhotoEventArgs : DbItemEventArgs {
-	private readonly bool metadata_changed;
-	public bool MetadataChanged {
-		get { return metadata_changed; }
-	}
-
-	private readonly bool data_changed;
-	public bool DataChanged {
-		get { return data_changed; }
-	}
-
-	public PhotoEventArgs (Photo photo, bool metadata_changed, bool data_changed)
-		: this (new Photo [] { photo }, metadata_changed, data_changed)
-	{
-	}
-
-	public PhotoEventArgs (Photo [] items, bool metadata_changed, bool data_changed)
-		: base (items)
-	{
-		this.metadata_changed = metadata_changed;
-		this.data_changed = data_changed;
-	}
-}
 
 public class PhotoStore : DbStore {
 	public int TotalPhotos {
@@ -149,21 +126,21 @@
 
 
 		Database.ExecuteNonQuery (
-			"CREATE TABLE photo_tags (     " +
-			"	photo_id      INTEGER, " +
-			"       tag_id        INTEGER  " +
+			"CREATE TABLE photo_tags (        " +
+			"	photo_id      INTEGER,    " +
+			"       tag_id        INTEGER,    " +
+			"       UNIQUE (photo_id, tag_id) " +
 			")");
 
 
 		Database.ExecuteNonQuery (
-			//WARNING: if you change this schema, reflect your changes 
-			//to Updater.cs, at revision 8.0
-			"CREATE TABLE photo_versions (    	" +
-			"       photo_id        INTEGER,  	" +
-			"       version_id      INTEGER,  	" +
-			"       name            STRING,    	" +
+			"CREATE TABLE photo_versions (		"+
+			"	photo_id	INTEGER,	" +
+			"	version_id	INTEGER,	" +
+			"	name		STRING,		" +
 			"	uri		STRING NOT NULL," +
-			"	protected	BOOLEAN		" +
+			"	protected	BOOLEAN, 	" +
+			"	UNIQUE (photo_id, version_id)	" +
 			")");
 	}
 
@@ -390,11 +367,10 @@
 	{
 		Photo [] photos = Query (tags, String.Empty, null, null);	
 
-		foreach (Photo photo in photos) {
+		foreach (Photo photo in photos)
 			photo.RemoveCategory (tags);
-			Commit (photo, true, false);
-		}
-		
+		Commit (photos);
+
 		foreach (Tag tag in tags)
 			Core.Database.Tags.Remove (tag);
 		
@@ -422,24 +398,12 @@
 		Remove (new Photo [] { (Photo)item });
 	}
 
-// Marking this obsolete causes a warning: Obsolete member `PhotoStore.Commit(FSpot.DbItem)' overrides non-obsolete member `DbStore.Commit(FSpot.DbItem)'.
-//	[Obsolete("WARNING! You should not use this one for photos, the events are not specific enough")]
 	public override void Commit (DbItem item)
 	{
-		throw new NotImplementedException("You should not use PhotoStore.Commit(DbItem) for photos, the events are not specific enough");
+		Commit (new Photo [] {item as Photo});
 	}
 
-	public void Commit (Photo photo, bool metadata_changed, bool data_changed)
-	{
-		Commit (new Photo [] { photo }, metadata_changed, data_changed);
-	}
-
-	public void Commit (Photo [] items, bool metadata_changed, bool data_changed)
-	{
-		Commit (items, new PhotoEventArgs (items, metadata_changed, data_changed));
-	}
-
-	public void Commit (Photo [] items, PhotoEventArgs args)
+	public void Commit (Photo [] items)
 	{
 		// Only use a transaction for multiple saves. Avoids recursive transactions.
 		bool use_transactions = !Database.InTransaction && items.Length > 1;
@@ -447,65 +411,85 @@
 		if (use_transactions)
 			Database.BeginTransaction ();
 
+		PhotosChanges changes = new PhotosChanges ();
 		foreach (DbItem item in items)
-			Update ((Photo)item);
+			changes |= Update ((Photo)item);
 
 		if (use_transactions)
 			Database.CommitTransaction ();
 
-		EmitChanged (items, args);
+		EmitChanged (items, new PhotoEventArgs (items, changes));
 	}
 
-	public void EmitChanged (Photo photo, bool metadata_changed, bool data_changed)
-	{
-		EmitChanged (new Photo [] { photo }, metadata_changed, data_changed);
-	}
-
-	public void EmitChanged (Photo [] items, bool metadata_changed, bool data_changed)
-	{
-		EmitChanged (items, new PhotoEventArgs (items, metadata_changed, data_changed));
-	}
-	
-	private void Update (Photo photo) {
+	private PhotoChanges Update (Photo photo) {
+		PhotoChanges changes = photo.Changes;
 		// Update photo.
-		Database.ExecuteNonQuery (new DbCommand (
-			"UPDATE photos SET description = :description, " + 
-			"default_version_id = :default_version_id, " + 
-			"time = :time, " + 
-			"uri = :uri, " +
-			"rating = :rating " +
-			"WHERE id = :id ",
-			"description", photo.Description,
-			"default_version_id", photo.DefaultVersionId,
-			"time", DbUtils.UnixTimeFromDateTime (photo.Time),
-			"uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
-			"rating", String.Format ("{0}", photo.Rating),
-			"id", photo.Id));
+		if (changes.DescriptionChanged || changes.DefaultVersionIdChanged || changes.TimeChanged || changes.UriChanged || changes.RatingChanged )
+			Database.ExecuteNonQuery (new DbCommand (
+				"UPDATE photos SET description = :description, " +
+				"default_version_id = :default_version_id, " +
+				"time = :time, " +
+				"uri = :uri, " +
+				"rating = :rating " +
+				"WHERE id = :id ",
+				"description", photo.Description,
+				"default_version_id", photo.DefaultVersionId,
+				"time", DbUtils.UnixTimeFromDateTime (photo.Time),
+				"uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
+				"rating", String.Format ("{0}", photo.Rating),
+				"id", photo.Id));
 
 		// Update tags.
-
-		Database.ExecuteNonQuery (new DbCommand ("DELETE FROM photo_tags WHERE photo_id = :id", "id", photo.Id));
-
-		foreach (Tag tag in photo.Tags) {
-			Database.ExecuteNonQuery (new DbCommand ("INSERT INTO photo_tags (photo_id, tag_id) " +
-                                         " VALUES (:photo_id, :tag_id)",
-                                         "photo_id", photo.Id, "tag_id", tag.Id));
-		}
+		if (changes.TagsRemoved != null)
+			foreach (Tag tag in changes.TagsRemoved)
+				Database.ExecuteNonQuery (new DbCommand (
+					"DELETE FROM photo_tags WHERE photo_id = :photo_id AND tag_id = :tag_id",
+					"photo_id", photo.Id,
+					"tag_id", tag.Id));
+
+		if (changes.TagsAdded != null)
+			foreach (Tag tag in changes.TagsAdded)
+				Database.ExecuteNonQuery (new DbCommand (
+					"INSERT OR IGNORE INTO photo_tags (photo_id, tag_id) " +
+					"VALUES (:photo_id, :tag_id)",
+					"photo_id", photo.Id,
+					"tag_id", tag.Id));
 
 		// Update versions.
-
-		Database.ExecuteNonQuery (new DbCommand ("DELETE FROM photo_versions WHERE photo_id = :id", "id", photo.Id));
-
-		foreach (uint version_id in photo.VersionIds) {
-			if (version_id == Photo.OriginalVersionId)
-				continue;
-			PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
-
-			Database.ExecuteNonQuery(new DbCommand ("INSERT INTO photo_versions (photo_id, version_id, name, uri, protected) " +
-							     "       VALUES (:photo_id, :version_id, :name, :uri, :is_protected)",
-							     "photo_id", photo.Id, "version_id", version.VersionId,
-							     "name", version.Name, "uri", version.Uri.ToString (), "is_protected", version.IsProtected));
-		}
+		if (changes.VersionsRemoved != null)
+			foreach (uint version_id in changes.VersionsRemoved)
+				Database.ExecuteNonQuery (new DbCommand (
+					"DELETE FROM photo_versions WHERE photo_id = :photo_id AND version_id = :version_id",
+					"photo_id", photo.Id,
+					"version_id", version_id));
+
+		if (changes.VersionsAdded != null)
+			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) " +
+					"VALUES (:photo_id, :version_id, :name, :uri, :is_protected)",
+					"photo_id", photo.Id,
+					"version_id", version_id,
+					"name", version.Name,
+					"uri", version.Uri.ToString (),
+					"is_protected", version.IsProtected));
+			}
+		if (changes.VersionsModified != null)
+			foreach (uint version_id in changes.VersionsModified) {
+				PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
+				Database.ExecuteNonQuery (new DbCommand (
+					"UPDATE photo_versions SET name = :name, " +
+					"uri = :uri, protected = :protected " +
+					"WHERE photo_id = :photo_id AND version_id = :version_id",
+					"name", version.Name,
+					"uri", version.Uri.ToString (),
+					"protected", version.IsProtected,
+					"photo_id", photo.Id,
+					"version_id", version_id));
+			}
+		photo.Changes = null;
+		return changes;
 	}
 	
 	// Dbus
@@ -594,7 +578,6 @@
 				photo.RollId = Convert.ToUInt32 (reader[4]);
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
 				photo.Rating = Convert.ToUInt32 (reader [6]);
-				
 				version_list.Add (photo);
 			}
 
@@ -617,6 +600,8 @@
 		} else {
 			//Console.WriteLine ("Skipped Loading Data");
 		}
+		foreach (Photo photo in version_list)
+			photo.Changes = null;
 
 		Log.DebugTimerPrint (timer, "Query took {0}");
 		return id_list.ToArray (typeof (Photo)) as Photo [];

Modified: trunk/src/PhotoVersionCommands.cs
==============================================================================
--- trunk/src/PhotoVersionCommands.cs	(original)
+++ trunk/src/PhotoVersionCommands.cs	Sun Jul 13 14:05:06 2008
@@ -104,7 +104,7 @@
 
 			try {
 				photo.DefaultVersionId = photo.CreateVersion (name, photo.DefaultVersionId, true);
-				store.Commit (photo, true, false);
+				store.Commit (photo);
 			} catch (Exception e) {
 					string msg = Catalog.GetString ("Could not create a new version");
 					string desc = String.Format (Catalog.GetString ("Received exception \"{0}\". Unable to create version \"{1}\""),
@@ -147,7 +147,7 @@
 			if (dialog.Run () == (int) ResponseType.Ok) {
 				try {
 					photo.DeleteVersion (photo.DefaultVersionId);
-					store.Commit (photo, true, true);
+					store.Commit (photo);
 				} catch (Exception e) {
 					// FIXME show error dialog.
 					string msg = Catalog.GetString ("Could not delete a version");
@@ -190,7 +190,7 @@
 
 			try {
 				photo.RenameVersion (photo.DefaultVersionId, new_name);
-				store.Commit (photo, true, false);
+				store.Commit (photo);
 			} catch (Exception e) {
 					string msg = Catalog.GetString ("Could not rename a version");
 					string desc = String.Format (Catalog.GetString ("Received exception \"{0}\". Unable to rename version to \"{1}\""),
@@ -218,7 +218,7 @@
 				foreach (uint version_id in photo.VersionIds) {
 					try {
 						new_parent.DefaultVersionId = new_parent.CreateReparentedVersion (photo.GetVersion (version_id) as PhotoVersion);
-						store.Commit (new_parent, true, false);
+						store.Commit (new_parent);
 					} catch (Exception e) {
 						Console.WriteLine (e);	
 					}
@@ -232,7 +232,7 @@
 						Console.WriteLine(e);
 					}
 				}
-				store.Commit (photo, true, false);
+				store.Commit (photo);
 				MainWindow.Toplevel.Database.Photos.Remove (photo);
 			}
 			return true;

Modified: trunk/src/PhotoView.cs
==============================================================================
--- trunk/src/PhotoView.cs	(original)
+++ trunk/src/PhotoView.cs	Sun Jul 13 14:05:06 2008
@@ -279,7 +279,8 @@
 				FSpot.SepiaTone sepia = new FSpot.SepiaTone (photo);
 				sepia.Image = View.CompletePixbuf ();
 				sepia.Adjust ();
-				Core.Database.Photos.Commit (photo, true, true);
+				photo.Changes.DataChanged = true;
+				Core.Database.Photos.Commit (photo);
 			} catch (System.Exception e) {
 				ShowError (e, photo); 
 			}
@@ -293,7 +294,8 @@
 				FSpot.Desaturate desaturate = new FSpot.Desaturate (photo);
 				desaturate.Image = View.CompletePixbuf ();
 				desaturate.Adjust ();
-				Core.Database.Photos.Commit (photo, true, true);
+				photo.Changes.DataChanged = true;
+				Core.Database.Photos.Commit (photo);
 			} catch (System.Exception e) {
 				ShowError (e, photo);
 			}
@@ -341,7 +343,8 @@
 				
 				bool create_version = photo.DefaultVersion.IsProtected;
 				photo.SaveVersion (edited, create_version);
-				((PhotoQuery)query).Commit (Item.Index, true, true);
+				photo.Changes.DataChanged = true;
+				((PhotoQuery)query).Commit (Item.Index);
 	
 				// FIXME the fact that the selection doesn't go away is a bug in ImageView, it should
 				// be fixed there.
@@ -368,7 +371,7 @@
 		{
 			if (commit_delay.IsPending) {
 				commit_delay.Stop ();
-				((PhotoQuery)query).Commit (changed_photo, true, false);
+				((PhotoQuery)query).Commit (changed_photo);
 			}
 			return true;
 		}

Modified: trunk/src/QueuedSqliteDatabase.cs
==============================================================================
--- trunk/src/QueuedSqliteDatabase.cs	(original)
+++ trunk/src/QueuedSqliteDatabase.cs	Sun Jul 13 14:05:06 2008
@@ -282,6 +282,7 @@
                     lock(command_queue) {
                         command = command_queue.Dequeue();
                     }
+		    //Log.Debug (command.CommandText);
                     command.Execute();
                 }
 

Modified: trunk/src/SingleView.cs
==============================================================================
--- trunk/src/SingleView.cs	(original)
+++ trunk/src/SingleView.cs	Sun Jul 13 14:05:06 2008
@@ -229,17 +229,15 @@
 		void HandleRotate90Command (object sender, System.EventArgs args) 
 		{
 			RotateCommand command = new RotateCommand (this.Window);
-			if (command.Execute (RotateDirection.Clockwise, new IBrowsableItem [] { image_view.Item.Current })) {
-				collection.MarkChanged (image_view.Item.Index);
-			}
+			if (command.Execute (RotateDirection.Clockwise, new IBrowsableItem [] { image_view.Item.Current }))
+				collection.MarkChanged (image_view.Item.Index, FullInvalidate.Instance);
 		}
 
 		void HandleRotate270Command (object sender, System.EventArgs args) 
 		{
 			RotateCommand command = new RotateCommand (this.Window);
-			if (command.Execute (RotateDirection.Counterclockwise, new IBrowsableItem [] { image_view.Item.Current })) {
-				collection.MarkChanged (image_view.Item.Index);
-			}
+			if (command.Execute (RotateDirection.Counterclockwise, new IBrowsableItem [] { image_view.Item.Current }))
+				collection.MarkChanged (image_view.Item.Index, FullInvalidate.Instance);
 		}		
 
 		private void HandleSelectionChanged (FSpot.IBrowsableCollection selection) 

Modified: trunk/src/TagStore.cs
==============================================================================
--- trunk/src/TagStore.cs	(original)
+++ trunk/src/TagStore.cs	Sun Jul 13 14:05:06 2008
@@ -11,6 +11,7 @@
 using FSpot;
 using FSpot.Jobs;
 using FSpot.Query;
+using FSpot.Utils;
 
 // FIXME: This is to workaround the currently busted GTK# bindings.
 using System.Runtime.InteropServices;
@@ -156,7 +157,7 @@
 
 		// Pass 1, get all the tags.
 
-		SqliteDataReader reader = Database.Query("SELECT id, name, is_category, sort_priority, icon FROM tags");
+		SqliteDataReader reader = Database.Query ("SELECT id, name, is_category, sort_priority, icon FROM tags");
 
 		while (reader.Read ()) {
 			uint id = Convert.ToUInt32 (reader [0]);
@@ -201,8 +202,11 @@
 
 		//Pass 3, set popularity
 		reader = Database.Query ("SELECT tag_id, COUNT (*) as popularity FROM photo_tags GROUP BY tag_id");
-		while (reader.Read ())
-			(Get (Convert.ToUInt32 (reader [0])) as Tag).Popularity = Convert.ToInt32 (reader [1]);
+		while (reader.Read ()) {
+			Tag t = Get (Convert.ToUInt32 (reader [0])) as Tag;
+			if (t != null)
+				t.Popularity = Convert.ToInt32 (reader [1]);
+		}
 		reader.Close ();
 
 		if (FSpot.Core.Database.Meta.HiddenTagId.Value != null)

Modified: trunk/src/TimeDialog.cs
==============================================================================
--- trunk/src/TimeDialog.cs	(original)
+++ trunk/src/TimeDialog.cs	Sun Jul 13 14:05:06 2008
@@ -7,21 +7,21 @@
 using FSpot.UI.Dialog;
 
 namespace FSpot {
-	public class TimeChangedEventArgs : PhotoEventArgs {
-		TimeSpan span;
-
-		public TimeChangedEventArgs (Photo [] items, TimeSpan span)
-			: base (items, true, false)
-		{
-			this.span = span;
-		}
-
-		public TimeSpan TimeSpan {
-			get {
-				return span;
-			}
-		}
-	}
+//	public class TimeChangedEventArgs : PhotoEventArgs {
+//		TimeSpan span;
+//
+//		public TimeChangedEventArgs (Photo [] items, TimeSpan span)
+//			: base (items, true, false)
+//		{
+//			this.span = span;
+//		}
+//
+//		public TimeSpan TimeSpan {
+//			get {
+//				return span;
+//			}
+//		}
+//	}
 
 	public class TimeDialog : GladeDialog 
 	{
@@ -169,7 +169,7 @@
 				System.Console.WriteLine ("XXXXX old: {0} new: {1} span: {2}", time, p.Time, span);
 			}
 			
-			db.Photos.Commit (photos, new TimeChangedEventArgs (photos, span));
+			db.Photos.Commit (photos);
 		}
 
 		private void SpaceByInterval ()
@@ -193,7 +193,7 @@
 				accum += span;
 			}
 			
-			db.Photos.Commit (photos, new TimeChangedEventArgs (photos, span));
+			db.Photos.Commit (photos);
 		}
 
 		void HandleSpinChanged (object sender, EventArgs args)

Modified: trunk/src/UI.Dialog/ColorDialog.cs
==============================================================================
--- trunk/src/UI.Dialog/ColorDialog.cs	(original)
+++ trunk/src/UI.Dialog/ColorDialog.cs	Sun Jul 13 14:05:06 2008
@@ -205,7 +205,7 @@
 							 transform);
 				
 				photo.SaveVersion (final, create_version);
-				((PhotoQuery)view.Query).Commit (view.Item.Index, true, true);
+				((PhotoQuery)view.Query).Commit (view.Item.Index);
 				final.Dispose ();
 			} catch (System.Exception e) {
 				string msg = Catalog.GetString ("Error saving adjusted photo");

Modified: trunk/src/Updater.cs
==============================================================================
--- trunk/src/Updater.cs	(original)
+++ trunk/src/Updater.cs	Sun Jul 13 14:05:06 2008
@@ -246,6 +246,34 @@
 				Execute ("UPDATE photos SET rating = 0 WHERE rating IS NULL");
 			});
 
+			// Update to version 15.0
+			AddUpdate (new Version (15,0), delegate () {
+				string tmp_photo_tags = MoveTableToTemp ("photo_tags");
+				Execute (
+					"CREATE TABLE photo_tags (        " +
+					"	photo_id      INTEGER,    " +
+					"       tag_id        INTEGER,    " +
+					"       UNIQUE (photo_id, tag_id) " +
+					")");
+				Execute (String.Format (
+					"INSERT OR IGNORE INTO photo_tags (photo_id, tag_id) " +
+					"SELECT photo_id, tag_id FROM {0}", tmp_photo_tags));
+				string tmp_photo_versions = MoveTableToTemp ("photo_versions");
+				Execute (
+					"CREATE TABLE photo_versions (		"+
+					"	photo_id	INTEGER,	" +
+					"	version_id	INTEGER,	" +
+					"	name		STRING,		" +
+					"	uri		STRING NOT NULL," +
+					"	protected	BOOLEAN, 	" +
+					"	UNIQUE (photo_id, version_id)	" +
+					")");
+				Execute (String.Format (
+					"INSERT OR IGNORE INTO photo_versions 		" +
+					"(photo_id, version_id, name, uri, protected)	" +
+					"SELECT photo_id, version_id, name, uri, protected FROM {0}", tmp_photo_versions));
+			});
+
 			// Update to version 14.0
 			//AddUpdate (new Version (14,0),delegate () {
 			//	do update here

Modified: trunk/src/UriCollection.cs
==============================================================================
--- trunk/src/UriCollection.cs	(original)
+++ trunk/src/UriCollection.cs	Sun Jul 13 14:05:06 2008
@@ -11,6 +11,7 @@
 using System;
 using System.IO;
 using System.Collections;
+using System.Collections.Generic;
 using System.Xml;
 
 namespace FSpot {
@@ -143,7 +144,7 @@
 
 		protected void LoadItems (FileInfo [] files)
 		{
-			ArrayList items = new ArrayList ();
+			List<IBrowsableItem> items = new List<IBrowsableItem> ();
 			foreach (FileInfo f in files) {
 				if (FSpot.ImageFile.HasLoader (f.FullName)) {
 					Console.WriteLine (f.FullName);

Modified: trunk/src/Widgets/Filmstrip.cs
==============================================================================
--- trunk/src/Widgets/Filmstrip.cs	(original)
+++ trunk/src/Widgets/Filmstrip.cs	Sun Jul 13 14:05:06 2008
@@ -486,7 +486,7 @@
 
 		void HandleCollectionItemsChanged (IBrowsableCollection coll, BrowsableEventArgs args)
 		{
-			if (!args.DataChanged)
+			if (!args.Changes.DataChanged)
 				return;
 			foreach (int item in args.Items)
 				thumb_cache.TryRemove (FSpot.ThumbnailGenerator.ThumbnailPath ((selection.Collection [item]).DefaultVersionUri));

Modified: trunk/src/Widgets/IconView.cs
==============================================================================
--- trunk/src/Widgets/IconView.cs	(original)
+++ trunk/src/Widgets/IconView.cs	Sun Jul 13 14:05:06 2008
@@ -258,7 +258,7 @@
 		private void HandleItemsChanged (FSpot.IBrowsableCollection sender, BrowsableEventArgs args)
 		{
 			foreach (int item in args.Items) {
-				if (args.DataChanged)
+				if (args.Changes.DataChanged)
 					UpdateThumbnail (item);
 				InvalidateCell (item);
 			}
@@ -326,11 +326,11 @@
 
 			}
 
-			public void MarkChanged (int item)
+			public void MarkChanged (int item, IBrowsableItemChanges changes)
 			{
 				// Forward the change event up to our parent
 				// we'll fire the event when the parent calls us back
-				parent.MarkChanged ((int) selected_cells [item]);
+				parent.MarkChanged ((int) selected_cells [item], changes);
 			}
 
 			private void HandleParentItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args)
@@ -353,7 +353,7 @@
 					return;
 
 				int [] items = (int [])local_ids.ToArray (typeof (int));
-				ItemsChanged (this, new BrowsableEventArgs (items, args.MetadataChanged, args.DataChanged));
+				ItemsChanged (this, new BrowsableEventArgs (items, args.Changes));
 			}
 
 			public int [] Ids {
@@ -1424,12 +1424,11 @@
 
 			switch (args.Event.Type) {
 			case EventType.TwoButtonPress:
-				if (args.Event.Button != 1
-					|| (args.Event.State &  (ModifierType.ControlMask
-				| ModifierType.ShiftMask)) != 0)
-				return;
+				if (args.Event.Button != 1 ||
+						(args.Event.State &  (ModifierType.ControlMask | ModifierType.ShiftMask)) != 0)
+					return;
 				if (DoubleClicked != null)
-					DoubleClicked (this, new BrowsableEventArgs (cell_num, false, false));
+					DoubleClicked (this, new BrowsableEventArgs (cell_num, null));
 				return;
 
 			case EventType.ButtonPress:
@@ -1544,9 +1543,8 @@
 				ToggleCell (FocusCell);
 				break;
 			case Gdk.Key.Return:
-				if (DoubleClicked == null)
-					break;
-				DoubleClicked (this, new BrowsableEventArgs (FocusCell, false, false));
+				if (DoubleClicked != null)
+					DoubleClicked (this, new BrowsableEventArgs (FocusCell, null));
 				break;
 			default:
 				args.RetVal = false;

Modified: trunk/src/Widgets/MetadataDisplay.cs
==============================================================================
--- trunk/src/Widgets/MetadataDisplay.cs	(original)
+++ trunk/src/Widgets/MetadataDisplay.cs	Sun Jul 13 14:05:06 2008
@@ -156,7 +156,7 @@
 		}
 		
 		internal void HandleSelectionItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args) {
-			if (!args.MetadataChanged)
+			if (!args.Changes.MetadataChanged)
 				return;
 
 			if (!Page.IsActive)

Modified: trunk/src/Widgets/TagEntry.cs
==============================================================================
--- trunk/src/Widgets/TagEntry.cs	(original)
+++ trunk/src/Widgets/TagEntry.cs	Sun Jul 13 14:05:06 2008
@@ -219,6 +219,9 @@
 				if (tagnames [i].Length == 0)
 					continue;
 
+				if (selected_photos_tagnames.Contains (tagnames [i]))
+					continue;
+
 				Tag t = tag_store.GetTagByName (tagnames [i]);
 
 				if (t != null) // Correct for capitalization differences



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