Index: extensions/ChangeLog
===================================================================
--- extensions/ChangeLog	(revisione 4152)
+++ extensions/ChangeLog	(copia locale)
@@ -1,3 +1,8 @@
+2008-07-02  Lorenzo Milesi <maxxer@yetopen.it>
+
+	* DirectoryWatch/*: media library watch extension, inspired by 
+	Alexander Hixon's extension for Banshee. Fix bgo#312613.
+
 2008-07-03  Ruben Vermeersch  <ruben@savanne.be>
 
 	* BeagleService/BeagleService.addin.xml:
Index: extensions/DirectoryWatch/DirectoryWatch.cs
===================================================================
--- extensions/DirectoryWatch/DirectoryWatch.cs	(revisione 0)
+++ extensions/DirectoryWatch/DirectoryWatch.cs	(revisione 0)
@@ -0,0 +1,172 @@
+/*
+ * DirectoryWatch.DirectoryWatch.cs
+ *
+ * Author(s):
+ *	Lorenzo Milesi <maxxer@yetopen.it>
+ * Sourced and inspired from Alexander Hixon's library watch extension for Banshee.
+ * FIXME: NEED TO HANDLE VERSIONS!!!!!!!!!!!
+ * 
+ * This is free software. See COPYING for details.
+ *
+ */
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+using FSpot;
+using FSpot.Extensions;
+using FSpot.Utils;
+using System.Runtime.InteropServices;
+
+namespace DirectoryWatch {
+	public class DirWatch : IService
+	{
+		private FileSystemWatcher watcher;
+		private PhotoStore store;
+		
+		private List <string> import_list = new List <string> ();
+		private uint update_timeout_id;
+		
+		public bool Start ()
+		{
+			uint timer = Log.InformationTimerStart ("Starting DirectoryWatch");
+			try {
+				store = Core.Database.Photos;		
+				StartWatcher ();
+			} catch {
+				Log.Warning ("unable to start DirectoryWatch");
+			}
+			Log.DebugTimerPrint (timer, "DirectoryWatch startup took {0}");
+			return true;
+		}
+
+		public bool Stop ()
+		{
+			uint timer = Log.InformationTimerStart ("Stopping DirectoryWatch");
+			watcher.EnableRaisingEvents = false;
+			Log.DebugTimerPrint (timer, "DirectoryWatch shutdown took {0}");	
+			return true;
+		}
+
+		public void SuspendWatch ()
+		{
+			Log.DebugFormat ("Suspending DirectoryWatch");
+			watcher.EnableRaisingEvents = false;
+		}
+
+		public void ResumeWatch ()
+		{
+			Log.DebugFormat ("Resuming DirectoryWatch");
+			watcher.EnableRaisingEvents = true;
+		}
+
+		private void StartWatcher ()
+		{
+			watcher = new FileSystemWatcher ();
+
+			// TODO: Subscribe to library location change event, and change watcher path
+			watcher.Path = FSpot.Global.PhotoDirectory;
+			Log.DebugFormat ("Watching in: {0}", FSpot.Global.PhotoDirectory);
+
+			watcher.Created += new FileSystemEventHandler (FileSystemEvent);
+			watcher.Deleted += new FileSystemEventHandler (FileSystemEvent);
+			watcher.Changed += new FileSystemEventHandler (FileSystemEvent);
+			watcher.Renamed += new RenamedEventHandler (RenamedEvent);
+			watcher.IncludeSubdirectories = true;
+
+			// Start watching
+			watcher.EnableRaisingEvents = true;
+		}
+
+		private void FileSystemEvent (object source, FileSystemEventArgs e)
+		{
+			Log.DebugFormat ("FileSystemEvent: type={0}, fullpath={1}, name={2}", e.ChangeType, e.FullPath, e.Name);
+			
+			if (e.ChangeType == WatcherChangeTypes.Created) {
+				// Created is called when either:
+				//  - a new file is created inside the watched directory; OR
+				//  - a file is moved from an outside directory inside the watched directory.
+				//    Note: the Renamed event is only fired for files inside the watch.
+				
+				import_list.Add (e.FullPath);
+			} else if (e.ChangeType == WatcherChangeTypes.Deleted) {
+				Photo photo = store.GetByUri (new Uri (e.FullPath));
+
+				//FIXME handle deletion of versions
+				if (photo != null) {
+					store.Remove (photo);
+					Log.DebugFormat ("Removed {0} from db!", photo.DefaultVersionUri);
+				}
+			} else if (e.ChangeType == WatcherChangeTypes.Changed) {
+				// First Changed event is OpenWrite, last is CloseWrite. There may
+				// be zero or more events in between these two, especially when copying.
+				
+				// Wait for one second of no activity before assuming files were
+				// copied OK and import them (useless importing an empty file)
+				if (update_timeout_id > 0) {
+					Log.DebugFormat ("'QueueImport' timeout reset.");
+					GLib.Source.Remove (update_timeout_id);
+					update_timeout_id = 0;
+				}
+
+				GLib.Timeout.Add (1200, new GLib.TimeoutHandler (QueueImport));
+			}
+		}
+
+		private void RenamedEvent (object source, RenamedEventArgs e)
+		{
+			if (System.IO.File.Exists (e.FullPath))
+			    RenamePhotoUri (new Uri (e.OldFullPath), new Uri (e.FullPath));
+/*
++            } else {
++                // Entire directory rename - recursively scan tree and update children
++                ScanPathForRenames (e.FullPath, e.OldFullPath, e.FullPath);
++            }
+*/
+		}
+        
+/*
++        private void ScanPathForRenames (string path, string oldname, string newname)
++        {
++            foreach (string directory in Directory.GetDirectories (path)) {
++                ScanPathForRenames (directory, oldname, newname);
++            }
++            
++            foreach (string file in Directory.GetFiles (path)) {
++                RenameTrackPath (Path.Combine (oldname, file.Substring (newname.Length + 1)), file);
++            }
++        }
+*/       
+		private bool QueueImport ()
+		{
+			Log.DebugFormat ("Importing {0} photos.", import_list.Count);
+			if (import_list.Count > 0) {
+				ImportCommand icommand = new ImportCommand (null);
+				int imported = icommand.ImportFromPaths (store, import_list.ToArray (), false);
+				Log.DebugFormat ("Correctly imported {0} photos", imported);
+				import_list.Clear ();
+			}
+			
+			return false;
+		}
+
+		private void RenamePhotoUri (Uri oldpath, Uri newpath)
+		{
+			Log.DebugFormat ("Renaming {0} => {1}", oldpath.ToString (), newpath.ToString ());
+
+			Photo photo = store.GetByUri (oldpath);
+
+			// Find the version we need to relocate
+			if (photo != null) {
+				Log.DebugFormat ("Searching {0} in {1} versions", photo.Name, photo.VersionIds.Length);
+				for (uint i = 1; i <= photo.VersionIds.Length; i ++) 
+					if ((photo.GetVersion (i) as PhotoVersion).Uri == oldpath) {
+						(photo.GetVersion (i) as PhotoVersion).Uri = newpath;
+						store.Commit (photo);
+					}
+			} else
+				Log.DebugFormat ("Couldn't get photo id from uri!");
+		}
+
+	}
+}
Index: extensions/DirectoryWatch/.gitignore
===================================================================
--- extensions/DirectoryWatch/.gitignore	(revisione 0)
+++ extensions/DirectoryWatch/.gitignore	(revisione 0)
@@ -0,0 +1,5 @@
+/Makefile
+/Makefile.in
+/*.dll
+/*.dll.mdb
+/*.mpack
Index: extensions/DirectoryWatch/DirectoryWatch.addin.xml
===================================================================
--- extensions/DirectoryWatch/DirectoryWatch.addin.xml	(revisione 0)
+++ extensions/DirectoryWatch/DirectoryWatch.addin.xml	(revisione 0)
@@ -0,0 +1,17 @@
+<Addin namespace="FSpot"
+	id="DirectoryWatch"
+	version="0.4.4.0"
+	name="Directory watch"
+	description="Watch photo directory for added, deleted and modified images"
+	author="Lorenzo Milesi"
+	url="http://f-spot.org/Extensions"
+	category="Services">
+	<Dependencies>
+		<Addin id="Core" version="0.4.4.0"/>
+	</Dependencies>
+	<Extension path = "/FSpot/Services">
+		<Service class="DirectoryWatch.DirWatch"/>
+	</Extension>
+</Addin>
+
+
Index: extensions/DirectoryWatch/Makefile.am
===================================================================
--- extensions/DirectoryWatch/Makefile.am	(revisione 0)
+++ extensions/DirectoryWatch/Makefile.am	(revisione 0)
@@ -0,0 +1,46 @@
+include $(top_srcdir)/Makefile.include
+
+PLUGIN_NAME = DirectoryWatch
+
+PLUGIN_MANIFEST = $(PLUGIN_NAME).addin.xml
+
+PLUGIN_ASSEMBLY = $(PLUGIN_NAME).dll
+
+PLUGIN_SOURCES =			\
+	$(srcdir)/DirectoryWatch.cs	
+
+
+REFS =					\
+	-r:../../src/f-spot.exe		\
+	-r:../../src/FSpot.Core.dll	\
+	-r:../../src/FSpot.Utils.dll	\
+	-pkg:glib-sharp-2.0		\
+	$(LINK_GIO)			
+
+PKGS =
+
+RESOURCES =				\
+	-resource:$(srcdir)/$(PLUGIN_MANIFEST)
+
+all: $(PLUGIN_ASSEMBLY)
+
+mpack: $(PLUGIN_ASSEMBLY)
+	mautil p $(PLUGIN_ASSEMBLY)
+
+$(PLUGIN_ASSEMBLY): $(PLUGIN_SOURCES) $(PLUGIN_MANIFEST)
+	$(CSC_LIB) -out:$@ $(CSC_DEFINES) $(PLUGIN_SOURCES) $(REFS) $(PKGS) $(ASSEMBLIES) $(RESOURCES)
+
+plugindir = $(pkglibdir)/extensions
+
+plugin_DATA =			\
+	$(PLUGIN_ASSEMBLY)
+
+EXTRA_DIST = 			\
+	$(PLUGIN_SOURCES)	\
+	$(PLUGIN_MANIFEST)	\
+	$(PLUGIN_NAME).glade
+
+CLEANFILES =			\
+	$(PLUGIN_ASSEMBLY)	\
+	$(PLUGIN_ASSEMBLY).mdb	\
+	*.mpack
Index: extensions/Makefile.am
===================================================================
--- extensions/Makefile.am	(revisione 4152)
+++ extensions/Makefile.am	(copia locale)
@@ -2,6 +2,7 @@
 	BeagleService		\
 	CDExport		\
 	DBusService		\
+	DirectoryWatch		\
 	DefaultExporters	\
 	GalleryExport		\
 	FlickrExport		\
Index: configure.in
===================================================================
--- configure.in	(revisione 4152)
+++ configure.in	(copia locale)
@@ -363,6 +363,7 @@
 extensions/CDExport/Makefile
 extensions/DBusService/Makefile
 extensions/DefaultExporters/Makefile
+extensions/DirectoryWatch/Makefile
 extensions/FlickrExport/Makefile
 extensions/FlickrExport/FlickrNet/Makefile
 extensions/GalleryExport/Makefile
Index: ChangeLog
===================================================================
--- ChangeLog	(revisione 4152)
+++ ChangeLog	(copia locale)
@@ -1,3 +1,7 @@
+2008-06-27  Lorenzo Milesi <maxxer@yetopen.it>
+
+	* configure.in: add DirectoryWatch extension
+
 2008-07-04  Lorenzo Milesi <maxxer@yetopen.it>
 
 	* src/PhotoStore.cs: extend GetByUri to search in photo versions too.
