Index: src/f-spot.glade
===================================================================
--- src/f-spot.glade	(revision 3000)
+++ src/f-spot.glade	(working copy)
@@ -9185,7 +9185,7 @@
 			      <property name="visible">True</property>
 			      <property name="label" translatable="yes">_Month</property>
 			      <property name="use_underline">True</property>
-			      <property name="active">True</property>
+			      <property name="active">False</property>
 			      <signal name="activate" handler="HandleArrangeByTime" last_modification_time="Fri, 20 Aug 2004 22:26:32 GMT"/>
 			    </widget>
 			  </child>
@@ -9195,7 +9195,7 @@
 			      <property name="visible">True</property>
 			      <property name="label" translatable="yes">_Folder</property>
 			      <property name="use_underline">True</property>
-			      <property name="active">False</property>
+			      <property name="active">True</property>
 			      <property name="group">month</property>
 			      <signal name="activate" handler="HandleArrangeByDirectory" last_modification_time="Fri, 20 Aug 2004 22:26:32 GMT"/>
 			    </widget>
@@ -9313,6 +9313,16 @@
 		  </child>
 
 		  <child>
+		    <widget class="GtkCheckMenuItem" id="find_duplicate">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Find Duplicates</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="HandleFindDuplicates" last_modification_time="Tue, 10 Aug 2004 07:08:24 GMT"/>
+		    </widget>
+		  </child>
+
+		  <child>
 		    <widget class="GtkCheckMenuItem" id="find_untagged">
 		      <property name="visible">True</property>
 		      <property name="label" translatable="yes">_Untagged Photos</property>
@@ -11405,6 +11415,44 @@
 	      <property name="fill">False</property>
 	    </packing>
 	  </child>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="recurse_check">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">Include subdirectories</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">True</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="duplicate_check">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">Include duplicates.</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="active">False</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
 	</widget>
 	<packing>
 	  <property name="padding">0</property>
Index: src/PhotoStore.cs
===================================================================
--- src/PhotoStore.cs	(revision 3000)
+++ src/PhotoStore.cs	(working copy)
@@ -158,7 +158,7 @@
 	private string directory_path;
 	private string name;
 
-	private string Path {
+	public string Path {
 		get {
 			return System.IO.Path.Combine (directory_path, name);
 		}
@@ -206,6 +206,16 @@
 		}
 	}
 
+	private byte[] md5sum;
+	public byte[] MD5Sum {
+		get {
+			return md5sum;
+		}
+		set {
+			md5sum = value;
+		}
+	}
+
 	// Version management
 	public const int OriginalVersionId = 1;
 	private uint highest_version_id;
@@ -564,7 +574,32 @@
 		return version;
 	}
 
+	public static byte[] GetMD5Sum (string filename)
+	{
+		if (!File.Exists(filename))
+		 	return new byte[0];
+
+		FileStream fs = null;
+
+		try {	 
+			fs = new FileStream(filename, FileMode.Open, FileAccess.Read);			
+			System.Security.Cryptography.MD5 md5ServiceProvider = new System.Security.Cryptography.MD5CryptoServiceProvider();
+
+			return md5ServiceProvider.ComputeHash(fs);
+		} finally {
+		 	fs.Close ();
+		}
+	}
+
 	// Constructor
+	public Photo (uint id, long unix_time, string directory_path, string name, string description, uint default_version_id, byte[] md5sum) 
+		: this (id, unix_time, directory_path, name)
+	{
+		this.description = description;
+		this.default_version_id = default_version_id;
+		this.md5sum = md5sum;  
+	}
+     
 	public Photo (uint id, long unix_time, string directory_path, string name)
 		: base (id)
 	{
@@ -574,6 +609,7 @@
 		this.name = name;
 
 		description = "";
+		md5sum = new byte[0]; 
 
 		// Note that the original version is never stored in the photo_versions table in the
 		// database.
@@ -581,10 +617,7 @@
 	}
 
 	public Photo (uint id, long unix_time, string path)
-		: this (id, unix_time,
-
-			System.IO.Path.GetDirectoryName (path),
-			System.IO.Path.GetFileName (path))
+		: this (id, unix_time, System.IO.Path.GetDirectoryName (path), System.IO.Path.GetFileName (path))
 	{
 	}
 
@@ -593,6 +626,10 @@
 public class PhotoStore : DbStore {
 	TagStore tag_store;
 
+	private Hashtable md5_cache;
+	public Hashtable MD5Cache {
+		get { return md5_cache;}
+	}
 
 	static int total_photos = 0;
 	public static int TotalPhotos {
@@ -668,6 +705,7 @@
 		: base (connection, false)
 	{
 		this.tag_store = tag_store;
+		this.md5_cache = new Hashtable ();
 		EnsureThumbnailDirectory ();
 
 		SqliteCommand command;
@@ -694,6 +732,8 @@
 			"       name               STRING NOT NULL,		   " +
 			"       description        TEXT NOT NULL,	           " +
 			"       default_version_id INTEGER NOT NULL		   " +
+			"       md5sum             STRING NOT NULL                 " +
+
 			")");
 
 
@@ -725,23 +765,28 @@
 		FSpot.ImageFile img = FSpot.ImageFile.Create (origPath);
 		long unix_time = DbUtils.UnixTimeFromDateTime (img.Date);
 		string description = img.Description != null  ? img.Description.Split ('\0') [0] : "";
+		string directory = System.IO.Path.GetDirectoryName (newPath);
+		string name = System.IO.Path.GetFileName (newPath);
+		byte[] md5sum = Photo.GetMD5Sum(newPath);
+        
 		SqliteCommand command = new SqliteCommand ();
 		command.Connection = Connection;
 
 		command.CommandText = String.Format ("INSERT INTO photos (time, " +
-						     "directory_path, name, description, default_version_id) " +
-						     "       VALUES ({0}, '{1}', '{2}', '{3}', {4})",
+						     "directory_path, name, description, default_version_id, md5sum) " +
+						     "       VALUES ({0}, '{1}', '{2}', '{3}', {4}, '{5}')",
 						     unix_time,
-						     SqlString (System.IO.Path.GetDirectoryName (newPath)),
-						     SqlString (System.IO.Path.GetFileName (newPath)),
+						     SqlString (directory),
+						     SqlString (name),
 						     SqlString (description),
-						     Photo.OriginalVersionId);
+						     Photo.OriginalVersionId,
+						     SqlString (Convert.ToBase64String (md5sum)));
 
 		command.ExecuteScalar ();
 		command.Dispose ();
 
 		uint id = (uint) Connection.LastInsertRowId;
-		Photo photo = new Photo (id, unix_time, newPath);
+		Photo photo = new Photo (id, unix_time, directory, name, description, Photo.OriginalVersionId, md5sum);
 		AddToCache (photo);
 		photo.Loaded = true;
 
@@ -753,6 +798,12 @@
 		return photo;
 	}
 
+	protected override void AddToCache (DbItem item)
+	{
+		base.AddToCache (item);
+		this.AddToMD5Cache (item as Photo); 
+	}
+
 	private void GetVersions (Photo photo)
 	{
 
@@ -814,20 +865,12 @@
 				
 				photo.AddVersionUnsafely (version_id, name);
 			}
-
-			/*
-			string directory_path = null;
-			if (reader [3] != null)
-				directory_path = reader [3].ToString ();
-			System.Console.WriteLine ("directory_path = {0}", directory_path);
-			*/
-
-
 		}
 	}
 
-	private void GetAllTags () {
-			SqliteCommand command = new SqliteCommand ();
+	private void GetAllTags () 
+	{
+		SqliteCommand command = new SqliteCommand ();
 		command.Connection = Connection;
 		command.CommandText = String.Format ("SELECT photo_id, tag_id " +
 						     "FROM photo_tags ");
@@ -931,7 +974,8 @@
 						     "       directory_path,                       " +
 						     "       name,                                 " +
 						     "       description,                          " +
-						     "       default_version_id                    " +
+						     "       default_version_id,                   " +
+						     "       md5sum                                " +
 						     "     FROM photos                             " +
 						     "     WHERE id = {0}                          ",
 						     id);
@@ -941,10 +985,11 @@
 			photo = new Photo (id,
 					   Convert.ToInt64 (reader [0]),
 					   reader [1].ToString (),
-					   reader [2].ToString ());
+					   reader [2].ToString (),
+					   reader[3].ToString (),
+					   Convert.ToUInt32 (reader[4]),
+					   Convert.FromBase64String (reader[5].ToString ()));
 
-			photo.Description = reader[3].ToString ();
-			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
 			AddToCache (photo);
 		}
 
@@ -974,7 +1019,8 @@
 		command.CommandText = String.Format ("SELECT id,                                   " +
 				                     "       time,                                 " +
 						     "       description,                          " +
-						     "       default_version_id                    " +
+						     "       default_version_id,                   " +
+						     "       md5sum                                " +
 						     "  FROM photos                                " +
 						     " WHERE directory_path = \"{0}\"              " +
 						     "   AND name = \"{1}\"                        ",
@@ -987,10 +1033,11 @@
 			photo = new Photo (Convert.ToUInt32 (reader [0]),
 					   Convert.ToInt64 (reader [1]),
 					   directory_path,
-					   filename);
+					   filename,
+					   reader[2].ToString(),
+					   Convert.ToUInt32 (reader[3]),
+					   Convert.FromBase64String (reader[5].ToString ()));
 
-			photo.Description = reader[2].ToString ();
-			photo.DefaultVersionId = Convert.ToUInt32 (reader[3]);
 			AddToCache (photo);
 		}
 
@@ -1033,6 +1080,7 @@
 
 			query_builder.Append (String.Format ("id = {0}", items[i].Id));
 			tv_query_builder.Append (String.Format ("photo_id = {0}", items[i].Id));
+			RemoveFromMD5Cache (items[i]);
 			RemoveFromCache (items[i]);
 			TotalPhotos--;			
 		}
@@ -1050,6 +1098,8 @@
 		Remove (new Photo [] { (Photo)item });
 	}
 
+	
+
 	public override void Commit (DbItem item)
 	{
 		DbItemEventArgs args = new DbItemEventArgs (item);
@@ -1075,11 +1125,15 @@
 
 		ExecuteSqlCommand (String.Format ("UPDATE photos SET description = '{0}',     " +
 						     "                  default_version_id = {1}, " +
-						     "                  time = {2} " +
-						     "              WHERE id = {3}",
+						     "                  time = {2}, " +
+						     "     md5sum = '{3}' " +
+
+						     "              WHERE id = {4}",
 						     SqlString (photo.Description),
 						     photo.DefaultVersionId,
 						     DbUtils.UnixTimeFromDateTime (photo.Time),
+						     SqlString (Convert.ToBase64String (photo.MD5Sum)),
+
 						     photo.Id));
 
 		// Update tags.
@@ -1107,6 +1161,49 @@
 							     photo.Id, version_id, SqlString (version_name)));
 		}
 	}
+
+	public bool IsDuplicate (Photo photo)
+	{
+		string md5 = Convert.ToBase64String(photo.MD5Sum);
+	 
+	 	if (!md5_cache.ContainsKey(md5))
+			return false;	
+	 
+	 	if (photo.Id == (uint)md5_cache[md5])
+		 	return false;
+
+		return true;
+	}
+
+	protected void AddToMD5Cache (Photo photo)
+	{
+		if (photo.MD5Sum.Length == 0)
+		 	return;
+	 
+		if (md5_cache[Convert.ToBase64String(photo.MD5Sum)] == null)
+			md5_cache.Add(Convert.ToBase64String(photo.MD5Sum), photo.Id);
+	}
+
+	protected void RemoveFromMD5Cache (Photo photo)
+	{
+		if (photo.MD5Sum.Length == 0)
+		 	return;
+	 
+		object id = md5_cache[Convert.ToBase64String(photo.MD5Sum)];
+		
+		if (id == null)
+			return;
+	 
+		if ((uint)id != photo.Id)
+			return;
+
+		Photo[] photos = QueryMD5Sum (photo);		
+
+		if (photos.Length == 0)
+			md5_cache.Remove (Convert.ToBase64String(photo.MD5Sum));
+		else
+			md5_cache[Convert.ToBase64String(photo.MD5Sum)] = photos[0].Id;
+	}
 	
 	public class DateRange 
 	{
@@ -1158,11 +1255,11 @@
 				photo = new Photo (id,
 						   Convert.ToInt64 (reader [1]),
 						   reader [2].ToString (),
-						   reader [3].ToString ());
+						   reader [3].ToString (),
+						   reader [4].ToString (),
+						   Convert.ToUInt32 (reader[5]),
+						   Convert.FromBase64String (reader [6].ToString ()));
 				
-				photo.Description = reader[4].ToString ();
-				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);		 
-				
 				version_list.Add (photo);
 			}
 
@@ -1197,7 +1294,8 @@
 						     "       photos.directory_path,              " +
 						     "       photos.name,                        " +
 						     "       photos.description,                 " +
-						     "       photos.default_version_id           " +
+						     "       photos.default_version_id,          " +
+						     "       photos.md5sum                       " +
 						     "     FROM photos                           " +
 						     "     WHERE directory_path = \"{0}\"", dir.FullName);
 
@@ -1222,6 +1320,38 @@
 		return Query (query_builder.ToString ());
 	}
 
+	public Photo[] QueryDuplicate (DateRange range)
+	{
+		string query = "SELECT * FROM photos ";
+
+		if (range != null) {
+			query = query + String.Format ("WHERE photos.time >= {0} AND photos.time <= {1} ",
+			                       DbUtils.UnixTimeFromDateTime (range.Start),
+			                       DbUtils.UnixTimeFromDateTime (range.End));
+		}
+
+		Photo[] photos = Query (query);
+
+		ArrayList duplicates = new ArrayList ();
+
+		foreach (Photo photo in photos) {
+			if (IsDuplicate (photo))
+				duplicates.Add (photo);
+		}
+
+		return duplicates.ToArray (typeof (Photo)) as Photo[];
+	}
+
+
+	private Photo[] QueryMD5Sum (Photo original)
+	{
+		string query = String.Format("SELECT * FROM photos WHERE md5sum = '{0}' AND id <> {1}",
+		                             Convert.ToBase64String(original.MD5Sum),
+		                             original.Id);
+
+		return Query (query);
+	}
+
 	public Photo [] Query (Tag [] tags, string extra_condition, DateRange range)
 	{
 		string query;
@@ -1250,7 +1380,8 @@
 				      "       photos.directory_path,              " +
 				      "       photos.name,                        " +
 				      "       photos.description,                 " +
-				      "       photos.default_version_id           " +
+				      "       photos.default_version_id,          " +
+				      "       photos.md5sum                       " +
 				      "     FROM photos                      ");
 		
 		if (range != null) {
Index: src/Db.cs
===================================================================
--- src/Db.cs	(revision 3000)
+++ src/Db.cs	(working copy)
@@ -56,7 +56,7 @@
 	protected Hashtable item_cache;
 	bool cache_is_immortal;
 
-	protected void AddToCache (DbItem item)
+	protected virtual void AddToCache (DbItem item)
 	{
 		if (item_cache.Contains (item.Id))
 			item_cache.Remove (item.Id);
Index: src/Updater.cs
===================================================================
--- src/Updater.cs	(revision 3000)
+++ src/Updater.cs	(working copy)
@@ -77,9 +77,58 @@
 				ExecuteNonQuery ("DELETE FROM exports WHERE export_type='fspot:Folder'");
 			});
 
+
+ 			// Update from version 3 to 4: add md5sum column to photos table
+ 			AddUpdate (new Version (4,0), delegate (SqliteConnection connection) {
+ 				System.Console.WriteLine("Adding MD5 field to photos");
+ 
+ 				string temp_table = MoveTableToTemp ("photos");
+ 
+ 				ExecuteNonQuery ("CREATE TABLE photos ( " +
+ 						"  id                 INTEGER PRIMARY KEY NOT NULL, " +
+ 						"  time               INTEGER NOT NULL,	" +
+ 						"  directory_path     STRING NOT NULL,		" +
+ 						"  name               STRING NOT NULL,		" +
+ 						"  description        TEXT NOT NULL,	    " +
+ 						"  default_version_id INTEGER NOT NULL,	" +
+ 						"  md5sum             STRING NOT NULL		" +
+ 						")");
+ 
+ 				ExecuteScalar (String.Format (  "INSERT INTO photos (id, time, directory_path, " + 
+ 								"                    name, description, default_version_id, md5sum)" + 
+ 								"SELECT id, time, directory_path, " +
+ 								"       name, description, default_version_id, '' " +
+ 								"FROM   {0} ", 
+ 								temp_table));
+ 
+ 				SqliteCommand command = new SqliteCommand ();
+ 				command.Connection = db.Connection;
+ 				command.CommandText = "SELECT id, directory_path, name FROM photos";
+ 				SqliteDataReader reader = command.ExecuteReader ();
+     
+ 				while (reader.Read ()) {
+ 					uint photo_id = Convert.ToUInt32 (reader [0]);
+ 					string full_path = System.IO.Path.Combine(reader[1].ToString(), reader[2].ToString());
+ 
+ 					byte[] md5sum = Photo.GetMD5Sum(full_path);
+ 
+ 					if (md5sum == null)
+ 					 	continue;
+ 
+ 					ExecuteScalar (String.Format (  "UPDATE photos " + 
+ 									"SET md5sum = '{0}' " +
+ 									"WHERE id = {1}",
+ 									Convert.ToBase64String(md5sum),
+ 									photo_id));
+ 					}
+ 
+ 					command.Dispose ();
+ 			}, true);
+ 					
+ 
 //TODO: please consider fixing bgo 324425 on the next update of the db.
-			// Update to version 4.0
-			//AddUpdate (new Version (4,0),delegate (SqliteConnection connection) {
+			// Update to version 4.1
+			//AddUpdate (new Version (4,1),delegate (SqliteConnection connection) {
 			//	do update here
 			//});
 
Index: src/PhotoQuery.cs
===================================================================
--- src/PhotoQuery.cs	(revision 3000)
+++ src/PhotoQuery.cs	(working copy)
@@ -63,6 +63,7 @@
 			set {
 				tags = value;
 				untagged = false;
+				duplicate = false;
 				RequestReload ();
 			}
 		}
@@ -106,6 +107,7 @@
 					untagged = value;
 
 					if (untagged) {
+						duplicate = false;
 						tags = null;
 						extra_condition = null;
 					}
@@ -115,10 +117,31 @@
 			}
 		}
 
+		private bool duplicate = false;
+		public bool Duplicate {
+			get {
+				return duplicate;
+			}
+			set {
+				if (duplicate != value) {
+					duplicate = value;
+
+					if (duplicate) {
+						untagged = false;
+						tags = null;
+						extra_condition = null;
+					}
+					RequestReload ();
+				}
+			}
+		}
+
 		public void RequestReload ()
 		{
 			if (untagged)
 				photos = store.QueryUntagged (range);
+			else if (duplicate)
+				photos = store.QueryDuplicate (range);
 			else
 				photos = store.Query (tags, extra_condition, range);
 
Index: src/ImportCommand.cs
===================================================================
--- src/ImportCommand.cs	(revision 3000)
+++ src/ImportCommand.cs	(working copy)
@@ -297,6 +297,7 @@
 	[Glade.Widget] Gtk.ScrolledWindow photo_scrolled;
 	[Glade.Widget] Gtk.CheckButton recurse_check;
 	[Glade.Widget] Gtk.CheckButton copy_check;
+	[Glade.Widget] Gtk.CheckButton duplicate_check;
 	[Glade.Widget] Gtk.Button ok_button;
 	[Glade.Widget] Gtk.Image tag_image;
 	[Glade.Widget] Gtk.Label tag_label;
@@ -310,9 +311,13 @@
 	Gtk.Window main_window;
 	FSpot.PhotoList collection;
 	bool copy;
+	bool include_duplicates;
 	SourceMenu menu;
 
 	int total;
+    
+	private int duplicates;
+
 	PhotoStore store;
 	FSpot.Delay step;
 	
@@ -322,7 +327,8 @@
 
 	FSpot.Delay idle_start; 
 
-	string loading_string;
+	string default_loading = Mono.Posix.Catalog.GetString ("Loading {0} of {1}");
+	string duplicate_loading = Mono.Posix.Catalog.GetString ("Importing {0} of {1}, {2} duplicate(s)");
 
 	string import_path;
 	public string ImportPath {
@@ -375,7 +381,6 @@
 		main_window = mw;
 		step = new FSpot.Delay (new GLib.IdleHandler (Step));
 		idle_start = new FSpot.Delay (new IdleHandler (Start));
-		loading_string = Catalog.GetString ("Loading {0} of {1}");
 	}
 
 	private void HandleDialogResponse (object obj, ResponseArgs args)
@@ -387,12 +392,16 @@
 		}
 	}
 
-	private void UpdateProgressBar (int count, int total)
+	private void UpdateProgressBar (int count, int total, int duplicates)
 	{
 		if (progress_bar == null)
 			return;
 
-		progress_bar.Text = String.Format (loading_string, count, total);
+		if (duplicates > 0)
+			progress_bar.Text = String.Format (duplicate_loading, count, total, duplicates);
+		else 
+			progress_bar.Text = String.Format (default_loading,   count, total);
+
 		progress_bar.Fraction = (double) count / System.Math.Max (total, 1);
 	}
 
@@ -424,19 +433,25 @@
 
 		if (photo == null || thumbnail == null) {
 			Console.WriteLine ("Could not import file");
-		} else {
-			//icon_scrolled.Visible = true;
-			collection.Add (photo);
-		
-			//grid.AddThumbnail (thumbnail);
+		} 
+	        else {
+			if (store.IsDuplicate (photo)) {
+				duplicates++;
+				if (include_duplicates)
+					collection.Add (photo);
+				else
+					store.Remove (photo);
+			}
+			else {
+				collection.Add (photo);
+			}
 
+			UpdateProgressBar (count, total, include_duplicates ? duplicates : 0);
 		}
 
 		if (thumbnail != null)
 			thumbnail.Dispose ();
 		
-		if (count < total)
-			UpdateProgressBar (count + 1, total);
 
 		if (ongoing && total > 0)
 			return true;
@@ -466,10 +481,11 @@
 		this.importer = imp;
 		AllowFinish = false;
 		
+		duplicates = 0;
 		total = importer.Prepare ();
 
 		if (total > 0)
-			UpdateProgressBar (1, total);
+			UpdateProgressBar (1, total, 0);
 		
 		collection.Clear ();
 		collection.Capacity = total;
@@ -553,6 +569,14 @@
 		idle_start.Start ();
 	}
 
+	private void HandleDuplicateToggled (object sender, System.EventArgs args)
+	{
+		this.Cancel ();
+		this.Dialog.Sensitive = false;
+	       
+		idle_start.Start ();
+	}
+
 	public int ImportFromFile (PhotoStore store, string path)
 	{
 		this.store = store;
@@ -569,6 +593,7 @@
 		//import_folder_entry.Activated += HandleEntryActivate;
 		recurse_check.Toggled += HandleRecurseToggled;
 		copy_check.Toggled += HandleRecurseToggled;
+		duplicate_check.Toggled += HandleDuplicateToggled;
 
 		menu = new SourceMenu (this);
 		source_option_menu.Menu = menu;
@@ -730,6 +755,10 @@
 		
 		if (copy_check != null)
 			copy = copy_check.Active;
+
+		duplicates = 0;
+		if (duplicate_check != null)
+		 	include_duplicates = duplicate_check.Active;
 		
 		bool recurse = true;
 		if (recurse_check != null)
@@ -742,7 +771,7 @@
 		total = importer.Prepare ();
 		
 		if (total > 0)
-			UpdateProgressBar (1, total);
+			UpdateProgressBar (1, total, duplicates);
 		
 		collection.Clear ();
 		collection.Capacity = total;
@@ -782,7 +811,7 @@
 	{
 		Db db = new Db (db_path, true);
 
-		ImportCommand command = new ImportCommand ();
+		ImportCommand command = new ImportCommand (main_window);
 
 		command.ImportFromPath (db.Photos, directory_path, true);
 
Index: src/MainWindow.cs
===================================================================
--- src/MainWindow.cs	(revision 3000)
+++ src/MainWindow.cs	(working copy)
@@ -97,6 +97,7 @@
 	[Glade.Widget] MenuItem clear_date_range;
 
 	[Glade.Widget] CheckMenuItem find_untagged;
+	[Glade.Widget] CheckMenuItem find_duplicate;
 	
 	// Tags
 	[Glade.Widget] MenuItem edit_selected_tag;
@@ -1566,6 +1567,7 @@
 			"Grahm Orr",
 			"Ewen Cheslack-Postava",
 			"Gabriel Burt",
+			"Thomas Van Machelen",
 			"Patanjali Somayaji",
 			"Matt Jones",
 			"Martin Willemoes Hansen",
@@ -2136,6 +2138,9 @@
 			find_untagged.Active = query.Untagged;
 
 		
+		if (find_duplicate.Active != query.Duplicate)
+			find_duplicate.Active = query.Duplicate;
+
 		clear_date_range.Sensitive = (query.Range != null);
 		UpdateStatusLabel ();
 	}
@@ -2504,6 +2509,14 @@
 
 		query.Untagged = !query.Untagged;
 	}
+
+	void HandleFindDuplicates (object sender, EventArgs args) {
+		if (query.Duplicate == find_duplicate.Active)
+			return;
+
+		query.Duplicate = !query.Duplicate;
+	}
+
 	
 	void OnPreferencesChanged (object sender, GConf.NotifyEventArgs args)
 	{
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 3000)
+++ ChangeLog	(working copy)
@@ -80,6 +80,24 @@
 	* src/FindBar.cs: Avoid showing Hidden photos unless explicitly told to do
 	so.  Fixes BGO #411973.
 
+2007-02-23  Thomas Van Machelen <thomas.vanmachelen@gmail.com>
+	Add duplicates detection, original patch by Alvaro del Castillo,
+	Thomas Van Machelen fixed comments of Larry Ewing, and Nuno Ferreira
+	updated it to svn trunk.
+
+	* src/f-spot.glade: add duplicate checkbox to import, add find
+	duplicates to Find Menu in MainWindow
+
+	* src/PhotoStore.cs: Add md5 sum to Photo and photos table
+
+	* src/Db.cs: make AddToCache overridable
+
+	* src/Updater.cs: add upgrade for photos table
+
+	* src/MainWindow.cs
+	* src/ImportCommand.cs
+	* src/PhotoQuery.cs: support for finding duplicates
+
 2007-02-23  Stephane Delcroix  <stephane@delcroix.org>
 
 	* src/FormClient.cs: expose the multipart property.
