Re: Duplicates patch new version - Check box in importing



Hi Stewart!

El vie, 30-09-2005 a las 18:45 +1000, Stewart Smith escribió:
> On Wed, 2005-09-28 at 06:04 +0200, Alvaro del Castillo wrote:
> > Some days ago I have modified the Duplicates patch in order to be more
> > user friendly. When you are importing photos you have a new checkbox in
> > the import dialog so you can enable or disable it in order to import or
> > not to import the duplicates photos. You can see it in this screenshot:
> 
> After applying this patch, f-spot would crash trying to do database foo.
> 

Yes, if you try to use the actual database format it will crash because
the patch needs a new field for the MD5 string. So you need to test the
patch removing your current database. This is a very bad thing because
you can't use F-Spot seriously until the database scheme is maintained
between versions. But F-Spot isn't yet for end user consumption as you
can read in the README, so I have followed this way.

> so, after removing the f-spot database and starting my import again, i
> got an error during import:

This is really strange because I have tested it a lot. 

Trying to reproduce your problem:

1. I have created a database with F-Spot 0.1.3 (0.1.3-1ubuntu1).

2. I have tried to use the patched version of F-Spot with this database:

Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object
in <0x00000> <unknown method>
in (wrapper managed-to-native)
Mono.Data.SqliteClient.Sqlite:sqlite_finalize (intptr,intptr&)
in <0x00176> Mono.Data.SqliteClient.SqliteCommand:ExecuteReader
(CommandBehavior behavior, Boolean want_results, System.Int32
rows_affected)
....

3. I have removed the database in order the patched F-Spot doesn't find
the database scheme without the needed field:

rm ~/.gnome2/f-spot/photos.db

4. I have launched the patched F-Spot and try to import some photos and
I have right now exactly the same problem. Time to find the reason:

The problem is updating the photo tags ... and it comes from the fact
that the Duplicate tag isn't created. In my developing environment I
have a database which already had the duplicate tag created in the past
so this is why I haven't found the problem.

I attach a modified patch with the problem solved. Apply it against the
current CVS.

Thanks a lot Stewart for catching the bug and testing the duplicates
patch.

Cheers

> 
> Unhandled Exception: System.NullReferenceException: Object reference not
> set to an instance of an object
> in <0x002fa> PhotoStore:Commit (.DbItem item)
> in <0x001f4> ImportCommand:Step ()
> in <0x00191> ImportCommand:DoImport (.ImportBackend imp)
> in <0x000c2> ImportCommand:Start ()
> in <0x00305> ImportCommand:HandleSourceChanged (System.Object sender,
> System.EventArgs args)
> in (wrapper delegate-invoke)
> System.MulticastDelegate:invoke_void_object_EventArgs
> (object,System.EventArgs)
> in <0x00096> GLib.Signal:voidObjectCallback (IntPtr handle, IntPtr gch)
> in (wrapper native-to-managed) GLib.Signal:voidObjectCallback
> (intptr,intptr)
> in <0x00000> <unknown method>
> in (wrapper managed-to-native) Gtk.Dialog:gtk_dialog_run (intptr)
> in <0x0001d> Gtk.Dialog:Run ()
> in <0x0064a> ImportCommand:ImportFromFile (.PhotoStore store,
> System.String path)
> in <0x0005a> MainWindow:HandleImportCommand (System.Object sender,
> System.EventArgs e)
> in (wrapper delegate-invoke)
> System.MulticastDelegate:invoke_void_object_EventArgs
> (object,System.EventArgs)
> in <0x00096> GLib.Signal:voidObjectCallback (IntPtr handle, IntPtr gch)
> in (wrapper native-to-managed) GLib.Signal:voidObjectCallback
> (intptr,intptr)
> in <0x00000> <unknown method>
> in (wrapper managed-to-native) Gtk.Application:gtk_main ()
> in <0x00007> Gtk.Application:Run ()
> in <0x00007> Gnome.Program:Run ()
> in <0x003ea> Driver:Main (System.String[] args)
> 
> 
> hope this helps.
using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace FSpot {
    public delegate void DuplicatesFinderEnd (Boolean success);

    public class DuplicatesFinder {
        private System.Threading.Thread    duplicates_thread = null;
        private FSpot.ThreadProgressDialog progress_dialog_duplicates;
        private ArrayList                  duplicates = null;
        private Boolean                    end_duplicates = false;
        private uint                       duplicates_timer = 0;
        private System.Threading.Thread    md5_thread = null;
        private FSpot.ThreadProgressDialog progress_dialog_md5;
        private Boolean                    end_computeMD5 = false;
        private uint                       computeMD5_timer = 0;

        // Shared data variables we need to work with
        private Db                         db;
        private int[]                      selected_ids;
        private FSpot.PhotoQuery           query;
    
        // To inform that the Finder has finished
        public event DuplicatesFinderEnd   SearchFinished;
    
        public DuplicatesFinder (Db                 db, 
                                 FSpot.PhotoQuery   query)

        {
            this.db = db;
            this.query = query;
        }

        public void CreateDuplicateTag () {
            Tag tag_duplicate = db.Tags.Duplicate;
            if (tag_duplicate == null) {
                Console.WriteLine ("Creating the Duplicate tag ...");
                tag_duplicate = db.Tags.CreateTag (null, "Duplicate");
                db.Tags.Duplicate = tag_duplicate;
                tag_duplicate.StockIconName = "f-spot-hidden.png";
                tag_duplicate.SortPriority = -11;
                db.Tags.Commit (tag_duplicate);
            } else {
                Console.WriteLine ("The duplicate tag already exists ...");
            }
        }

        public void startFind (int [] selected_ids) {
            this.selected_ids = selected_ids;
            md5_thread = 
                new System.Threading.Thread (new System.Threading.ThreadStart (this.ComputeMD5));
            md5_thread.Name = Mono.Posix.Catalog.GetString ("Creating image unique identifiers");
            progress_dialog_md5 = new 
                FSpot.ThreadProgressDialog (md5_thread, selected_ids.Length);
            progress_dialog_md5.Start();
            StartComputeMD5Timer ();
        }

        private bool HandleDuplicatesTimer ()
        {
            Console.WriteLine ("Duplicates Timer ...");
            if (end_duplicates || !duplicates_thread.IsAlive) {
                if (db.Tags.Duplicate == null) {
                    CreateDuplicateTag ();
                }
                end_duplicates = false;
                duplicates_timer = 0;
                foreach (int num in duplicates) {
                    Console.WriteLine ("Tagging duplicate photo {0}", num);
                    query.Photos[num].AddTag (db.Tags.Duplicate);
                    query.Commit (num);
                }
                if (SearchFinished != null) {
                    SearchFinished (true);
                }
                return false;
            }
            return true;
        }

        private void StartDuplicatesTimer ()
        {
            if (duplicates_timer == 0)
                duplicates_timer = 
                    GLib.Timeout.Add (100, new GLib.TimeoutHandler (HandleDuplicatesTimer));
        }

        private bool HandleComputeMD5Timer ()
        {
            Console.WriteLine ("MD5 Timer ...");
            if (md5_thread.IsAlive) {
                Console.WriteLine ("The MD5 thread is alive ...");
            }
            if (end_computeMD5 || !md5_thread.IsAlive) {
                if (progress_dialog_md5 != null) {
                    progress_dialog_md5.Destroy ();
                }
                computeMD5_timer = 0;
                if (end_computeMD5) {
                    end_computeMD5 = false;
                    /* duplicates_thread = new System.Threading.Thread 
                        (new System.Threading.ThreadStart (this.FindDuplicates));
                    duplicates_thread.Name = Mono.Posix.Catalog.GetString ("Finding Duplicates");
                    progress_dialog_duplicates = 
                    new FSpot.ThreadProgressDialog (duplicates_thread, selected_ids.Length);
                    progress_dialog_duplicates.Start (); */
                    StartDuplicatesTimer (); 
                    lock (this) {
                        end_duplicates = true;
                    }
                } else {
                    if (SearchFinished != null) {
                        SearchFinished (false);
                    }
                }
                return false;
            }
            return true;
        }

        private void StartComputeMD5Timer ()
        {
            if (computeMD5_timer == 0)
                computeMD5_timer = 
                    GLib.Timeout.Add (1000, new GLib.TimeoutHandler (HandleComputeMD5Timer));
        }


        public static string PhotoMD5 (Photo photo) {
            FileStream fs = new FileStream(photo.Path, FileMode.Open, FileAccess.Read);
            MD5 md5ServiceProvider = new MD5CryptoServiceProvider();
            byte[] md5 = md5ServiceProvider.ComputeHash(fs);
            
            StringBuilder hash = new StringBuilder();
            for (int pos = 0; pos < md5.Length; pos++) {
                hash.Append(md5[pos].ToString("X2").ToLower());
            }
            return hash.ToString ();
        }

        /* FIXME: We should use a cache of all photos MD5? */
        public static bool IsDuplicate (Photo photo) {
            Photo[] photos;
            photos = MainWindow.Toplevel.Database.Photos.Query (null, null);
            if (photos == null) {
                Console.WriteLine ("No photos in duplicates ...");
                return false;
            }
            if (photo.MD5Sum == null) {
                photo.MD5Sum = PhotoMD5 (photo);
            }
            bool  duplicate = false;
            foreach (Photo p in photos) {
                if (p.MD5Sum == null) {
                    p.MD5Sum = PhotoMD5 (p);
                }
                //Console.WriteLine ("Looking: {0},{1} {2},{3}", 
                //                   p.Name, p.MD5Sum, photo.Name, photo.MD5Sum);
                if (p.MD5Sum.CompareTo(photo.MD5Sum) == 0 &&
                    p.Id != photo.Id) {
                    //Console.WriteLine ("DUPLICATES");
                    duplicate = true;
                    break;
                }
            }            
            return duplicate;
        } 

        // Executed in a different thread
        private void ComputeMD5 () {
            // A really simple implementation: Build hash table with MD5 of images
            // and we later use it to find duplicates. If the MD5 is in the hash table
            // we don't add it. We have only the "original" in this table.
            Hashtable photos_md5 = new Hashtable ();
            duplicates = new ArrayList ();

            int counter = 1;
            foreach (int num in selected_ids) {
                Photo photo = query.Photos [num];
                progress_dialog_md5.Message = System.String.Format 
                    (Mono.Posix.Catalog.GetString ("Creating unique identifier for {0}"), photo.Name);
                progress_dialog_md5.Fraction = counter / (double) selected_ids.Length;
                progress_dialog_md5.ProgressText = System.String.Format (Mono.Posix.Catalog.GetString ("{0} of {1}"), counter, selected_ids.Length);
                if (string.Compare(photo.MD5Sum, "") != 0) {
                    // It is a duplicate. We should mark it.
                    if (photos_md5[photo.MD5Sum] != null) {
                        duplicates.Add (num);
                    } else {
                        photos_md5.Add (photo.MD5Sum, num);
                    }
                    counter++;
                    continue;
                }
                // Computing time is measured for testing purposes
                try {
                    long startTime = DateTime.Now.Ticks;
                    FileStream fs = new FileStream(photo.Path, FileMode.Open, FileAccess.Read);
                    MD5 md5ServiceProvider = new MD5CryptoServiceProvider();
                    byte[] md5 = md5ServiceProvider.ComputeHash(fs);

                    StringBuilder hash = new StringBuilder();
                    for (int pos = 0; pos < md5.Length; pos++) {
                        hash.Append(md5[pos].ToString("X2").ToLower());
                    }
                    long endTime = DateTime.Now.Ticks;
                    TimeSpan timeTaken = new TimeSpan(endTime - startTime);
                    Console.WriteLine("MD5 compute: {0}", timeTaken.ToString());
                    photo.MD5Sum = hash.ToString ();
                    db.Photos.Commit (photo);
                    counter++;                    
                    // It is a duplicate. We should mark it.
                    if (photos_md5[photo.MD5Sum] != null) {
                        duplicates.Add (num);
                    } else {
                        photos_md5.Add (photo.MD5Sum, num);
                    }
                } catch (Exception ex) {
                    Console.WriteLine ("Problems creating MD5: {0} ", photo.Path);
                    Console.WriteLine (ex);
                }
            }
            progress_dialog_md5.Message = Mono.Posix.Catalog.GetString ("Done Creating Unique Identifiers for Photos");
            progress_dialog_md5.Fraction = 1.0;
            progress_dialog_md5.ProgressText = Mono.Posix.Catalog.GetString ("All unique identifiers created.");
            progress_dialog_md5.ButtonLabel = Gtk.Stock.Ok;
            lock (this) {
                end_computeMD5 = true;
            }
        }
    }
}
Index: src/ImportCommand.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/ImportCommand.cs,v
retrieving revision 1.39
diff -u -b -B -p -r1.39 ImportCommand.cs
--- src/ImportCommand.cs	22 Aug 2005 10:47:34 -0000	1.39
+++ src/ImportCommand.cs	4 Oct 2005 04:27:22 -0000
@@ -362,6 +365,7 @@ public class ImportCommand : FSpot.Glade
 	[Glade.Widget] Gtk.ScrolledWindow photo_scrolled;
 	[Glade.Widget] Gtk.CheckButton attach_check;
 	[Glade.Widget] Gtk.CheckButton recurse_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;
@@ -378,6 +382,11 @@ public class ImportCommand : FSpot.Glade
 	bool copy;
 
 	int total;
+    
+    private int          duplicates;
+    private long         total_md5;
+    private long         total_import;
+
 	PhotoStore store;
 
 	FSpot.Delay step;
@@ -390,6 +399,8 @@ public class ImportCommand : FSpot.Glade
 	{
 		main_window = mw;
 		step = new FSpot.Delay (10, new GLib.IdleHandler (Step));
+        total_md5 = 0;
+        total_import = 0;
 	}
 
 	private void HandleDialogResponse (object obj, ResponseArgs args)
@@ -401,12 +412,16 @@ public class ImportCommand : FSpot.Glade
 		}
 	}
 
-	private void UpdateProgressBar (int count, int total)
+	private void UpdateProgressBar (int count, int total, int duplicates)
 	{
 		if (progress_bar == null)
 			return;
-
+        if (duplicates > 0) {
+            progress_bar.Text = String.Format ("Importing {0} of {1}, {2} duplicate/s", 
+                                               count-duplicates, total-duplicates, duplicates);
+        } else {
 		progress_bar.Text = String.Format ("Importing {0} of {1}", count, total);
+        }
 		progress_bar.Fraction = (double) count / System.Math.Max (total, 1);
 	}
 
@@ -431,6 +446,10 @@ public class ImportCommand : FSpot.Glade
 		Pixbuf thumbnail;
 		int count;
 		bool ongoing = true;
+        TimeSpan timeTakenMD5 = new TimeSpan();;
+
+        //Console.WriteLine ("Start importing the photo ...");
+        long startTime = DateTime.Now.Ticks;
 
 		if (importer == null)
 			return false;
@@ -440,14 +459,48 @@ public class ImportCommand : FSpot.Glade
 		if (thumbnail == null) {
 			Console.WriteLine ("Could not import file");
 		} else {
-			//icon_scrolled.Visible = true;
+            long startTimeMD5 = DateTime.Now.Ticks;
+            photo.MD5Sum = FSpot.DuplicatesFinder.PhotoMD5 (photo);            
+            store.Commit (photo);
+            long endTimeMD5 = DateTime.Now.Ticks;
+            total_md5 += endTimeMD5 - startTimeMD5;
+            timeTakenMD5 = new TimeSpan (endTimeMD5 - startTimeMD5);
+            //Console.WriteLine ("Total time for MD5 {0}", timeTakenMD5.ToString());
+
+            if (FSpot.DuplicatesFinder.IsDuplicate (photo)) {
+                photo.AddTag (MainWindow.Toplevel.Database.Tags.Duplicate);
+                store.Commit (photo);
+            }
+            
+            if (FSpot.DuplicatesFinder.IsDuplicate (photo)) {
+                //Console.WriteLine ("Photo: {0} duplicated\n", photo.Name);
+                duplicates++;
+                if (duplicate_check.Active) {                    
 			collection.Add (photo);
+                } else {
+                    //Console.WriteLine ("Removing duplicate ...");
+                    store.Remove (photo);
+                }
+            } else {                
+                //Console.WriteLine ("Importing Photo: {0}", photo.Name);
+                collection.Add (photo);
+            }
 		
 			//grid.AddThumbnail (thumbnail);
-			UpdateProgressBar (count, total);
+            if (duplicate_check.Active) {
+                UpdateProgressBar (count, total, 0);
+            } else {
+                UpdateProgressBar (count, total, duplicates);
+            }
 			thumbnail.Dispose ();
 		}
 
+        long endTime = DateTime.Now.Ticks;
+        total_import += endTime - startTime;
+        TimeSpan timeTaken = new TimeSpan(endTime - startTime);
+        //Console.WriteLine ("Total time importing the photo {0} ({1} MD5)\n", 
+        //                   timeTaken.ToString(), timeTakenMD5.ToString ());        
+
 		if (ongoing && total > 0)
 			return true;
 		else {
@@ -473,8 +526,9 @@ public class ImportCommand : FSpot.Glade
 		this.importer = imp;
 		AllowFinish = false;
 
+        duplicates = 0;
 		total = importer.Prepare ();
-		UpdateProgressBar (0, total);
+		UpdateProgressBar (0, total, 0);
 		
 		collection.Clear ();
 		collection.Capacity = total;
@@ -492,10 +546,20 @@ public class ImportCommand : FSpot.Glade
 			}
 		}
 
+        TimeSpan timeTaken = new TimeSpan(total_import);
+        TimeSpan timeTakenMD5 = new TimeSpan(total_md5);
+        //Console.WriteLine ("Total time importing the photos {0} ({1} MD5)\n", 
+        //                   timeTaken.ToString(), timeTakenMD5.ToString ()); 
+
 		FSpot.ThumbnailGenerator.Default.PopBlock ();
 		
-		if (importer != null)
+		if (importer != null) {
+			try { 
 			importer.Finish ();
+            } catch (System.Exception e) {
+                Console.WriteLine ("Exception finishing import ..." + e);
+            }
+        }
 		
 		importer = null;
 
@@ -619,6 +683,15 @@ public class ImportCommand : FSpot.Glade
 		this.Start ();
 	}
 
+    
+    private void HandleDuplicateToggled (object sender, System.EventArgs args)
+	{
+		this.Cancel ();
+		while (Application.EventsPending ())
+			Application.RunIteration ();
+		this.Start ();
+	}
+
 	public int ImportFromFile (PhotoStore store, string path)
 	{
 		this.store = store;
@@ -643,6 +716,7 @@ public class ImportCommand : FSpot.Glade
 	        AllowFinish = false;
 		
 		recurse_check.Toggled += HandleRecurseToggled;
+        duplicate_check.Toggled += HandleRecurseToggled;
 
 		tag_option_menu.Menu = tagmenu;
 		SourceMenu menu = new SourceMenu ();
@@ -786,7 +860,7 @@ public class ImportCommand : FSpot.Glade
 	{
 		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
===================================================================
RCS file: /cvs/gnome/f-spot/src/MainWindow.cs,v
retrieving revision 1.218
diff -u -b -B -p -r1.218 MainWindow.cs
--- src/MainWindow.cs	2 Oct 2005 07:13:38 -0000	1.218
+++ src/MainWindow.cs	4 Oct 2005 04:27:22 -0000
@@ -13,10 +13,11 @@ using System.Text.RegularExpressions;
 using LibGPhoto2;
 
 public class MainWindow {
-        public static MainWindow Toplevel;
+    public static MainWindow Toplevel = null;
 
 	Db db;
 
+	
 	TagSelectionWidget tag_selection_widget;
 	[Glade.Widget] Gtk.Window main_window;
 	[Glade.Widget] Gtk.VBox left_vbox;
@@ -68,6 +69,8 @@ public class MainWindow {
 	[Glade.Widget] MenuItem remove_tag;
 	[Glade.Widget] MenuItem find_tag;
 	
+    [Glade.Widget] MenuItem find_duplicates;
+	
 	[Glade.Widget] Scale zoom_scale;
 
 	[Glade.Widget] VPaned info_vpaned;
@@ -1686,6 +1689,28 @@ public class MainWindow {
 	void HandleClearDateRange (object sender, EventArgs args) {
 		query.Range = null;
 	}
+
+	void HandleFindDuplicates (object sender, EventArgs args) {
+        find_duplicates.Sensitive = false;
+        FSpot.DuplicatesFinder finder = new FSpot.DuplicatesFinder (db, query);
+        finder.CreateDuplicateTag ();
+        tag_selection_widget.Update ();
+        if (!PhotoSelectionActive()) {
+            icon_view.SelectAllCells ();
+        }
+        System.Console.WriteLine ("Looking for duplicates ...");
+        finder.SearchFinished += new FSpot.DuplicatesFinderEnd (HandleEndDuplicates);
+        finder.startFind (SelectedIds());
+    }
+
+    void HandleEndDuplicates (Boolean success) {
+        if (success) {
+            tag_selection_widget.Select (db.Tags.Duplicate);
+            UpdateQuery ();
+            SetViewMode (ModeType.IconView);
+        }
+        find_duplicates.Sensitive = true;
+    }
 
 	// Version Id updates.
 
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/src/Makefile.am,v
retrieving revision 1.43
diff -u -b -B -p -r1.43 Makefile.am
--- src/Makefile.am	29 Sep 2005 10:37:09 -0000	1.43
+++ src/Makefile.am	4 Oct 2005 04:27:22 -0000
@@ -16,6 +16,7 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/Delay.cs			\
 	$(srcdir)/DirectoryAdaptor.cs		\
 	$(srcdir)/DirectoryCollection.cs	\
+	$(srcdir)/DuplicatesFinder.cs	\
 	$(srcdir)/Exif.cs			\
 	$(srcdir)/ExifUtils.cs			\
 	$(srcdir)/FlickrExport.cs		\
Index: src/PhotoStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/PhotoStore.cs,v
retrieving revision 1.71
diff -u -b -B -p -r1.71 PhotoStore.cs
--- src/PhotoStore.cs	1 Sep 2005 10:01:52 -0000	1.71
+++ src/PhotoStore.cs	4 Oct 2005 04:27:22 -0000
@@ -28,6 +28,7 @@ public class Photo : DbItem, IComparable
 		return Compare (this, photo);
 	}
 	
+    // FIXME: With md5sum field this could be easy
 	public static int Compare (Photo photo1, Photo photo2)
 	{
 		int result = photo1.Id.CompareTo (photo2.Id);
@@ -137,6 +138,16 @@ public class Photo : DbItem, IComparable
 		}
 	}
 
+    private string md5sum;
+    public string MD5Sum {
+		get {
+			return md5sum;
+		}
+		set {
+			md5sum = value;
+		}
+	}
+
 	// Version management
 	public const int OriginalVersionId = 1;
 	private uint highest_version_id;
@@ -393,6 +404,7 @@ public class Photo : DbItem, IComparable
 		this.name = name;
 
 		description = "";
+        md5sum      = "";
 
 		// Note that the original version is never stored in the photo_versions table in the
 		// database.
@@ -493,7 +505,8 @@ public class PhotoStore : DbStore {
 			"       directory_path     STRING NOT NULL,		   " +
 			"       name               STRING NOT NULL,		   " +
 			"       description        TEXT NOT NULL,	           " +
-			"       default_version_id INTEGER NOT NULL		   " +
+			"       default_version_id INTEGER NOT NULL,		     " +
+            "       md5sum             STRING NOT NULL		         " +
 			")";
 
 		command.ExecuteNonQuery ();
@@ -538,8 +551,8 @@ public class PhotoStore : DbStore {
 		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}, '')                                       ",
 						     unix_time,
 						     SqlString (System.IO.Path.GetDirectoryName (path)),
 						     SqlString (System.IO.Path.GetFileName (path)),
@@ -728,7 +741,8 @@ public class PhotoStore : DbStore {
 						     "       directory_path,                       " +
 						     "       name,                                 " +
 						     "       description,                          " +
-						     "       default_version_id                    " +
+						     "       default_version_id,                   " +
+                             "       md5sum                                " +
 						     "     FROM photos                             " +
 						     "     WHERE id = {0}                          ",
 						     id);
@@ -742,6 +756,7 @@ public class PhotoStore : DbStore {
 
 			photo.Description = reader[3].ToString ();
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
+            photo.MD5Sum = reader[5].ToString ();
 			AddToCache (photo);
 		}
 
@@ -895,11 +910,13 @@ public class PhotoStore : DbStore {
 
 		SqliteCommand command = new SqliteCommand ();
 		command.Connection = Connection;
-		command.CommandText = String.Format ("UPDATE photos SET description = '{0}',     " +
-						     "                  default_version_id = {1} " +
-						     "              WHERE id = {2}",
+		command.CommandText = String.Format ("UPDATE photos SET description = '{0}'," +
+						     "                default_version_id = {1}," +
+                             "                md5sum = '{2}'"+                
+						     "                WHERE id = {3}",
 						     SqlString (photo.Description),
 						     photo.DefaultVersionId,
+                             SqlString (photo.MD5Sum),  
 						     photo.Id);
 		command.ExecuteNonQuery ();
 		command.Dispose ();
@@ -913,6 +930,7 @@ public class PhotoStore : DbStore {
 		command.Dispose ();
 
 		foreach (Tag tag in photo.Tags) {
+            
 			command = new SqliteCommand ();
 			command.Connection = Connection;
 			command.CommandText = String.Format ("INSERT INTO photo_tags (photo_id, tag_id) " +
@@ -1004,6 +1022,7 @@ public class PhotoStore : DbStore {
 				
 				photo.Description = reader[4].ToString ();
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);		 
+                photo.MD5Sum = reader[6].ToString ();
 				
 				version_list.Add (photo);
 			}
@@ -1043,7 +1062,8 @@ public class PhotoStore : DbStore {
 						     "       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);
 
@@ -1084,7 +1104,8 @@ public class PhotoStore : DbStore {
 				      "       photos.directory_path,              " +
 				      "       photos.name,                        " +
 				      "       photos.description,                 " +
-				      "       photos.default_version_id           " +
+				      "       photos.default_version_id,          " +
+                      "       photos.md5sum                       " +
 				      "     FROM photos                      ");
 		
 		if (range != null) {
Index: src/TagStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagStore.cs,v
retrieving revision 1.19
diff -u -b -B -p -r1.19 TagStore.cs
--- src/TagStore.cs	27 Sep 2005 03:15:05 -0000	1.19
+++ src/TagStore.cs	4 Oct 2005 04:27:22 -0000
@@ -257,6 +257,16 @@ public class TagStore : DbStore {
 		}
 	}
 
+    private Tag duplicate;
+	public Tag Duplicate {
+		get {
+			return duplicate;
+		}
+        set {
+            duplicate = value;
+        }
+	}
+
 	// In this store we keep all the items (i.e. the tags) in memory at all times.  This is
 	// mostly to simplify handling of the parent relationship between tags, but it also makes it
 	// a little bit faster.  We achieve this by passing "true" as the cache_is_immortal to our
@@ -290,6 +300,9 @@ public class TagStore : DbStore {
 			
 			if (tag.Name == "Hidden")
 				hidden = tag;
+
+            if (tag.Name == "Duplicate")
+                duplicate = tag;            
 		}
 
 		reader.Close ();
@@ -354,6 +367,12 @@ public class TagStore : DbStore {
 		this.hidden = hidden_tag;
 		Commit (hidden_tag);
 
+        Tag duplicate_tag = CreateTag (RootCategory, "Duplicate");
+		duplicate_tag.StockIconName = "f-spot-hidden.png";
+		duplicate_tag.SortPriority = -11;
+		this.duplicate = duplicate_tag;
+		Commit (duplicate_tag);
+
 		Tag people_category = CreateCategory (RootCategory, "People");
 		people_category.StockIconName = "f-spot-people.png";
 		people_category.SortPriority = -8;
@@ -455,6 +474,11 @@ public class TagStore : DbStore {
 		    category.Children.Length > 0)
 			throw new InvalidTagOperationException (category, "Cannot remove category that contains children");
 
+        // FIXME: Hack!
+        if (string.Compare (((Tag) item).Name, "Duplicate") == 0) {
+            duplicate = null;
+        }
+        
 		RemoveFromCache (item);
 		
 		((Tag)item).Category = null;
Index: src/f-spot.glade
===================================================================
RCS file: /cvs/gnome/f-spot/src/f-spot.glade,v
retrieving revision 1.109
diff -u -b -B -p -r1.109 f-spot.glade
--- src/f-spot.glade	3 Oct 2005 22:36:36 -0000	1.109
+++ src/f-spot.glade	4 Oct 2005 04:27:23 -0000
@@ -7129,6 +7129,15 @@ Photo Details</property>
 		  </child>
 
 		  <child>
+		    <widget class="GtkMenuItem" id="find_duplicates">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Find Duplicates</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="HandleFindDuplicates" last_modification_time="Tue, 10 Aug 2004 07:08:24 GMT"/>
+		    </widget>
+		  </child>
+
+		  <child>
 		    <widget class="GtkSeparatorMenuItem" id="separator15">
 		      <property name="visible">True</property>
 		    </widget>
@@ -9841,6 +9850,12 @@ Photo Details</property>
 	  <property name="spacing">6</property>
 
 	  <child>
+	    <widget class="GtkHBox" id="hbox70">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
 	    <widget class="GtkCheckButton" id="recurse_check">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
@@ -9851,6 +9866,32 @@ Photo Details</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">10</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>


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