? XmpTagsImportPatch.diff
? icons/f-spot-imported-xmp-tags.png
? icons/f-spot-xmp-tagged.png
? libeog/access.lo
? libeog/accessible-image-view-factory.lo
? libeog/accessible-image-view.lo
? libeog/cursors.lo
? libeog/eog-file-selection.lo
? libeog/eog-image.lo
? libeog/eog-pixbuf-util.lo
? libeog/eog-scroll-view.lo
? libeog/image-view.lo
? libeog/libeog-marshal.lo
? libeog/libfspoteog.la
? libeog/ui-image.lo
? libeog/uta.lo
? libeog/zoom.lo
? libfspot/f-image-view.lo
? libfspot/f-jpeg-utils.lo
? libfspot/f-marshal.lo
? libfspot/f-pixbuf-save.lo
? libfspot/f-pixbuf-unsharp.lo
? libfspot/f-pixbuf-utils.lo
? libfspot/f-screen-utils.lo
? libfspot/f-utils.lo
? libfspot/libfspot.la
? libjpegtran/jpeg-data.lo
? libjpegtran/jpeg-marker.lo
? libjpegtran/jpegtran.lo
? libjpegtran/libfspotjpegtran.la
? libjpegtran/transupp.lo
? po/.intltool-merge-cache
? po/stamp-it
? src/XmpTagsImporter.cs
? src/XmpTagsMetadata.cs
? tools/NONE
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/f-spot/ChangeLog,v
retrieving revision 1.1532
diff -u -p -r1.1532 ChangeLog
--- ChangeLog	30 Jul 2006 18:17:12 -0000	1.1532
+++ ChangeLog	4 Aug 2006 11:14:32 -0000
@@ -1,3 +1,17 @@
+2006-08-03  Bengt Thuree <bengt@thuree.com> 
+ 
+ 	* : The below changes fixes bug #342137
+  	* src/XmpTagsMetadata.cs: Reads the XMP tags from a photo during import
+ 	
+ 	* src/XmpTagsImporter.cs: Creates XMP tags during import
+ 	
+ 	* src/FileImportBackend.cs: Calls XmpTagsImporter during import.
+ 	
+ 	* src/Makefile.am: Added XmpTagsMetadata.cs and XmpTagsImporter.cs
+ 	
+ 	* src/ImportCommand.cs: disable DisplayTags since otherwise we get a crash. 
+ 	Cancel do not seem to remove all...
+ 
 2006-07-30  Larry Ewing  <lewing@novell.com>
 
 	* src/Loupe.cs: clean up the loupe a little more, calculate the
Index: icons/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/icons/Makefile.am,v
retrieving revision 1.19
diff -u -p -r1.19 Makefile.am
--- icons/Makefile.am	28 Mar 2006 05:00:33 -0000	1.19
+++ icons/Makefile.am	4 Aug 2006 11:14:32 -0000
@@ -9,6 +9,7 @@ noinst_DATA =					\
 	f-spot-favorite.png			\
 	f-spot-fullscreen.png			\
 	f-spot-hidden.png			\
+	f-spot-imported-xmp-tags.png		\
 	f-spot-loading.png			\
 	f-spot-new-tag.png			\
 	f-spot-other.png			\
Index: src/FileImportBackend.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/FileImportBackend.cs,v
retrieving revision 1.36
diff -u -p -r1.36 FileImportBackend.cs
--- src/FileImportBackend.cs	18 Jul 2006 23:17:42 -0000	1.36
+++ src/FileImportBackend.cs	4 Aug 2006 11:14:33 -0000
@@ -3,6 +3,8 @@ using Gtk;
 using Gnome;
 using System.Collections;
 using System;
+using FSpot;
+using FSpot.Xmp;
 using System.IO;
 
 public class ImportException : System.Exception {
@@ -13,12 +15,14 @@ public class ImportException : System.Ex
 
 public class FileImportBackend : ImportBackend {
 	PhotoStore store;
+	TagStore tag_store = FSpot.Core.Database.Tags;
 	bool recurse;
 	bool copy;
 	string [] base_paths;
 	Tag [] tags;
 
 	int count;
+	XmpTagsImporter xmptags;
 
 	ArrayList import_info;
 	Stack directories;
@@ -39,8 +43,7 @@ public class FileImportBackend : ImportB
 		
 		public ImportInfo (string original)
 		{
-			original_path
- = original;
+			original_path = original;
 		        destination_path= null;
 			Photo = null;
 		}
@@ -114,6 +117,8 @@ public class FileImportBackend : ImportB
 		}	
 
 		directories = new Stack ();
+		xmptags = new XmpTagsImporter (store, tag_store);
+
 		return import_info.Count;
 	}
 
@@ -195,6 +200,7 @@ public class FileImportBackend : ImportB
 
 		// FIXME Need to get the EXIF info etc.
 		ImportInfo info = (ImportInfo)import_info [this.count];
+		bool needs_commit = false;
 
 		try {
 			string destination = info.OriginalPath;
@@ -226,8 +232,13 @@ public class FileImportBackend : ImportB
 				foreach (Tag t in tags) {
 					photo.AddTag (t);
 				}
-				store.Commit(photo);
+				needs_commit = true;
 			}
+
+			needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
+
+			if (needs_commit)
+				store.Commit(photo);
 			
 			info.Photo = photo;
 		} catch (System.Exception e) {
@@ -273,6 +284,8 @@ public class FileImportBackend : ImportB
 					info.Delete ();
 			}
 		}
+		// Clean up just created tags
+		xmptags.Cancel();
 	}
 
 	public override void Finish ()
@@ -286,6 +299,7 @@ public class FileImportBackend : ImportB
 		}
 
 		import_info = null;
+		xmptags.Finish();
 		count = 0;
 	}
 
Index: src/ImportCommand.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/ImportCommand.cs,v
retrieving revision 1.63
diff -u -p -r1.63 ImportCommand.cs
--- src/ImportCommand.cs	9 Jun 2006 22:53:02 -0000	1.63
+++ src/ImportCommand.cs	4 Aug 2006 11:14:33 -0000
@@ -346,7 +346,6 @@ public class ImportCommand : FSpot.Glade
 
 			if (item.Source is BrowseSource) {
 				string path = ChoosePath ();
-				
 				if (path != null) {
 					SourceItem path_item = new SourceItem (new VfsSource (path));
 					menu.Prepend (path_item);
@@ -512,7 +511,6 @@ public class ImportCommand : FSpot.Glade
 	public string ChoosePath ()
 	{
 		string path = null;
-
 		CompatFileChooserDialog file_selector =
 			new CompatFileChooserDialog ("Import", this.Dialog,
 						     CompatFileChooserDialog.Action.SelectFolder);
@@ -529,7 +527,6 @@ public class ImportCommand : FSpot.Glade
 		if ((ResponseType) response == ResponseType.Ok) {
 			path = file_selector.Filename;
 		}
-
 		file_selector.Destroy ();
 		return path;
 	}
@@ -578,6 +575,7 @@ public class ImportCommand : FSpot.Glade
 
 		collection = new FSpot.PhotoList (new Photo [0]);
 		tray = new FSpot.ScalingIconView (collection);
+		tray.DisplayTags = false;		// Otherwise deleting just created XMP tags will crash.
 		tray.Selection.Changed += HandleTraySelectionChanged;
 		icon_scrolled.SetSizeRequest (200, 200);
 		icon_scrolled.Add (tray);
@@ -659,7 +657,6 @@ public class ImportCommand : FSpot.Glade
 					store.Commit (p);
 				}
 			}
-
 			this.Dialog.Destroy ();
 			return collection.Count;
 		} else {
@@ -699,6 +696,7 @@ public class ImportCommand : FSpot.Glade
 	{
 		idle_start.Stop ();
 		step.Stop ();
+		
 		if (importer != null) {
 			importer.Cancel ();
 			importer = null;
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/src/Makefile.am,v
retrieving revision 1.64
diff -u -p -r1.64 Makefile.am
--- src/Makefile.am	18 Jul 2006 23:16:24 -0000	1.64
+++ src/Makefile.am	4 Aug 2006 11:14:34 -0000
@@ -119,6 +119,8 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/TagSelectionDialog.cs		\
 	$(srcdir)/X3fFile.cs			\
 	$(srcdir)/XmpFile.cs			\
+	$(srcdir)/XmpTagsImporter.cs		\
+	$(srcdir)/XmpTagsMetadata.cs		\
 	$(srcdir)/main.cs
 
 ASSEMBLIES =					\
@@ -147,6 +149,7 @@ RESOURCES =										\
 	-resource:$(top_srcdir)/icons/f-spot-events.png,f-spot-events.png		\
 	-resource:$(top_srcdir)/icons/f-spot-favorite.png,f-spot-favorite.png 		\
 	-resource:$(top_srcdir)/icons/f-spot-hidden.png,f-spot-hidden.png 		\
+	-resource:$(top_srcdir)/icons/f-spot-imported-xmp-tags.png,f-spot-imported-xmp-tags.png	\
 	-resource:$(top_srcdir)/icons/f-spot-loading.png,f-spot-loading.png		\
 	-resource:$(top_srcdir)/icons/f-spot-logo.png,f-spot-logo.png			\
 	-resource:$(top_srcdir)/icons/f-spot-new-tag.png,f-spot-new-tag.png		\
--- /dev/null	2006-05-23 00:25:23.000000000 +1000
+++ src/XmpTagsImporter.cs	2006-08-04 11:58:57.000000000 +1000
@@ -0,0 +1,230 @@
+// XmpTagsImporter.cs: Creates tags based on embedded XMP tags in the photo/sidecar.
+//
+// Author:
+//   Bengt Thuree (bengt@thuree.com)
+//
+// (C) 2006 Bengt Thuree
+// 
+
+using Gtk;
+using System;
+using System.Collections;
+using System.IO;
+using System.Xml;
+using FSpot.Xmp;
+
+namespace FSpot.Xmp {
+	public class XmpTagsImporter {
+		private PhotoStore photo_store;
+		private TagStore tag_store;
+		private Stack tags_created;
+		private Gtk.Window parent;
+		static private string LastImportStr = "Imported Tags";
+		static private string LastImportIcon = "f-spot-imported-xmp-tags.png";
+		static private string CityStr = "City";
+		static private string CountryStr = "Country";
+		static private string LocationStr = "Location";
+		static private string StateStr = "State";
+		static private string PlacesIcon = "f-spot-places.png";
+		static private string XMPSidecarStr = "Tags in a XMP Sidecar";
+
+		private class TagInfo {
+			// This class contains the Root tag name, and its Icon name (if any)
+			string tag_name;
+			string icon_name; 
+		       
+			public string TagName {
+				get { return tag_name; }
+			}
+			
+			public string IconName {
+				get { return icon_name; }
+			}
+
+			public bool HasIcon {
+				get { return icon_name != null; }
+			}
+
+			public TagInfo (string t_name, string i_name)
+			{
+			        tag_name = t_name;
+				icon_name = i_name;
+			}
+			
+			public TagInfo (string t_name)
+			{
+			        tag_name = t_name;
+				icon_name = null;
+			}
+		} // TagInfo
+		
+		TagInfo li_root_tag; // This is the Last Import root tag
+		// The following are the various sub tags under Last Import.
+		TagInfo li_subroot_country, li_subroot_city, li_subroot_state, li_subroot_loc;
+					
+		public XmpTagsImporter (PhotoStore photo_store, TagStore tag_store)
+		{
+			this.photo_store = photo_store;
+			this.tag_store = tag_store;
+			tags_created = new Stack ();
+			// Prepare the Last Import root tag
+			li_root_tag = new TagInfo (LastImportStr, LastImportIcon);
+			// Prepare possible sub root's under the Last Import root tag
+			li_subroot_loc = new TagInfo (LocationStr, PlacesIcon);
+			li_subroot_country = new TagInfo (CountryStr, PlacesIcon);
+			li_subroot_city = new TagInfo (CityStr, PlacesIcon);
+			li_subroot_state = new TagInfo (StateStr, PlacesIcon);
+		}
+
+		private void AddTagToPhoto (Photo photo, string new_tag_name, TagInfo sub_tag_root)
+		{
+			if (new_tag_name != null) {
+				// Check if a tag exists for this new_tag_name
+				Tag tag = tag_store.GetTagByName (new_tag_name);
+
+				// If not, make sure the rootstr tag exists and add this new_tag_name under it
+				if (tag == null) {
+					Tag root_tag = tag_store.GetTagByName (Mono.Posix.Catalog.GetString (li_root_tag.TagName));
+					if (root_tag == null) {
+						root_tag = tag_store.GetTagByName (li_root_tag.TagName);
+
+						if (root_tag == null) {
+							root_tag = tag_store.CreateCategory (
+								tag_store.RootCategory, 
+								Mono.Posix.Catalog.GetString (li_root_tag.TagName));
+								if (li_root_tag.HasIcon)
+									root_tag.StockIconName = li_root_tag.IconName;
+							tags_created.Push (root_tag);
+						}
+					}
+					// If we should have a sub root, add it now.
+					if (sub_tag_root != null)	{
+						Tag subroot_tag = tag_store.GetTagByName (Mono.Posix.Catalog.GetString (sub_tag_root.TagName));
+						if (subroot_tag == null) {
+							subroot_tag = tag_store.GetTagByName (sub_tag_root.TagName);
+
+							if (subroot_tag == null) {
+								subroot_tag = tag_store.CreateCategory (
+									root_tag as Category, 
+									Mono.Posix.Catalog.GetString (sub_tag_root.TagName));
+									if (sub_tag_root.HasIcon)
+										subroot_tag.StockIconName = sub_tag_root.IconName;									
+								tags_created.Push (subroot_tag);
+							}
+						}
+						root_tag = subroot_tag; // Ensure we attach to subroot_tag 
+					} // end adding subroot
+					
+					// add the new tag, and put it under the root/subroot
+					tag = tag_store.CreateCategory (root_tag as Category, new_tag_name);
+					tags_created.Push (tag);
+				}
+				
+				// Now we have the tag for this place, add the photo to it
+				photo.AddTag (tag);
+			} // if new_tag_name != null
+		}
+
+		private void AddTagToPhoto (Photo photo, string [] new_tag_names, TagInfo sub_tag_root)
+		{
+			if (new_tag_names != null)
+				foreach (string tag in new_tag_names)
+					AddTagToPhoto (photo, tag, sub_tag_root);
+		}
+		
+
+		
+		public bool Import (Photo photo, string path, string orig_path)
+		{
+			bool needs_commit = false; // Default to no XMP data embedded in picture
+
+			XmpTagsMetadata xmd = new XmpTagsMetadata (path, orig_path);
+			
+			// Read the XMP tags (embedded tags takes priority)
+			xmd.Read_Sidecar_tags();	// first read xmp tags from a possible sidecar file
+			xmd.Read_Embedded_tags();	// second read the tags embedded in the photo (exif/xmp etc)
+			
+			// If no tags (EXIF/XMP etc) were found, return false
+			if (xmd.EmptyTags())
+				return needs_commit;
+
+			// Ok, we have XMP data embeeded in the photo. 
+			needs_commit = true;
+
+			// F-Spot do not keep Headline/Caption but only a description field, 
+			// so we need to combine the two headline/caption to one field (with a " : " between)
+			// Also, there are at least two different options here. So we assume that if
+			// something is set, we should use it. Start to check for the title, then description.
+
+			// Since F-Spot do not handle the EXIF description to good yet, we leave this one out.
+//			if (xmd.EXIF_Description != null) 
+//				photo.Description = xmd.EXIF_Description;
+
+			// F-Spot only stores the description into XMP User Comment, so if this one is set, we use it.
+			if (xmd.adobe_UserComment != null) {
+				photo.Description = xmd.adobe_UserComment;
+			}
+
+			// We want to construct the following : Description = <Headline> :: <Caption>         
+
+			// only check for more title/comment if you still do not have one.
+			if ((photo.Description == null) || (photo.Description.Length == 0)) {
+				if (xmd.ps_Headline != null)
+					photo.Description = xmd.ps_Headline;
+				// Lets add the Caption to the existing Description (Headline).
+				if (xmd.ps_Caption != null)
+					photo.Description += (( (xmd.ps_Headline == null) ? "" : " :: ") + xmd.ps_Caption);
+			}	
+			
+			// only check for more title/comment if you still do not have one. 
+			if ((photo.Description == null) || (photo.Description.Length == 0)) {
+				if (xmd.dc_Title != null)
+					photo.Description = xmd.dc_Title;
+				// Lets add the Description  to the existing Description (Title).
+				if (xmd.dc_Description != null)
+					photo.Description += (( (xmd.dc_Title == null) ? "" : " :: ") + xmd.dc_Description);
+			}
+			
+			if (xmd.Time != DateTime.MinValue)
+				photo.Time = xmd.Time;
+
+			if (xmd.State != null)
+				AddTagToPhoto (photo, xmd.State, li_subroot_state);
+			if (xmd.City != null)
+				AddTagToPhoto (photo, xmd.City, li_subroot_city);
+			if (xmd.Country != null)
+				AddTagToPhoto (photo, xmd.Country, li_subroot_country);
+			if (xmd.Location != null)
+				AddTagToPhoto (photo, xmd.Location, li_subroot_loc);
+			if (xmd.Keywords != null)
+				AddTagToPhoto (photo, xmd.Keywords, null);
+			
+			// Ok, not good. But at least one indication which photo has tags in a sidecar.
+			// FIXME : Should be changed to possible an internal representation later.
+			//if (xmd.AnySidecarTags())
+			//	AddTagToPhoto (photo, Mono.Posix.Catalog.GetString (XMPSidecarStr), null);
+			
+			return needs_commit;
+		}
+
+		public void Cancel()
+		{
+			// User have cancelled the import.
+			// Remove all created tags
+			while (tags_created.Count > 0) 
+				tag_store.Remove ((DbItem) tags_created.Pop());
+			
+			// Clear the tags_created array
+			tags_created.Clear();
+		}
+
+		public void Finish()
+		{
+			// Clear the tags_created array, since we do not need it anymore.
+			tags_created.Clear();		
+		}
+
+
+	}
+
+} // namespace
--- /dev/null	2006-05-23 00:25:23.000000000 +1000
+++ src/XmpTagsMetadata.cs	2006-08-04 11:54:36.000000000 +1000
@@ -0,0 +1,603 @@
+// #define enable_debug
+//
+// XmpTagsMetaData.cs: Collects a number of embedded XMP tags from a photo
+//	Will try to find an external sidecar file (<image file name>.xmp) both in
+//	same place as photo, as well as from the imported location of the photo.
+//
+// 	Read the sidecar tags first, and then the embedded tags. (which is controlled by the calling class)
+//	Doing this, we will not loose any tags and the embedded tags takes priority.
+//
+// Author:
+//   Bengt Thuree (bengt@thuree.com)
+//	With great help from Stephane and Cosme
+//
+// (C) 2006 Bengt Thuree
+// 
+
+using System;
+using System.IO;
+using System.Xml;
+using FSpot;
+using FSpot.Xmp;
+using SemWeb;
+
+namespace FSpot.Xmp {
+	public class XmpTagsMetadata {
+		private bool xmptags_sidecar_exist;	// Sets to true if a xmp sidecar (<image file name>.xmp) 
+							// is found (with photo, or import location)
+		private bool xmptags_found_sidecar_tag; // Indicates if the sidecar file contained tags or not.
+		private bool xmptags_found_xmp_tags;	// Sets to true if EXIF or XMP tags exists.
+		private string xmptags_photofile;	// full path to image file
+		private string xmptags_sidecarfile;	// full path to sidecar file
+		
+		private System.Collections.ArrayList items_list; // Temporary storage of items before we store them in the right place.
+
+		
+
+		private DateTime time = DateTime.MinValue;	
+		public DateTime Time {		
+			get { return time; }	
+		}
+
+		private string exif_description;	public string Exif_Description	{get { return exif_description; }	}
+
+		// http://ns.adobe.com/photoshop/1.0
+		private string city;			public string City 		{get { return city; }}
+		private string country;			public string Country		{get { return country; }	}
+		private string state;			public string State		{get { return state; }	}
+		private string ps_headline;		public string ps_Headline	{get { return ps_headline; }	}
+		private string ps_caption;		public string ps_Caption 	{get { return ps_caption; }	}
+		private string urgency;			public string Urgency 		{get { return urgency; }	}
+		private string captionwriter;		public string CaptionWriter	{get { return captionwriter; }	}
+		private string credit;			public string Credit		{get { return credit; }	}
+		private string category;		public string Category		{get { return category; }	}
+		// SupplementalCategories - See Keywords
+
+
+		// http://ns.adobe.com/tiff/1.0
+		private string make;			public string Make 		{get { return make; }	}
+		private string model;			public string Model 		{get { return model; }	}
+		private string orientation;		public string Orientation 	{get { return orientation; }	}
+		private string resolutionunit;		public string ResolutionUnit 	{get { return resolutionunit; }	}
+
+		// http://ns.adobe.com/exif/1.0					
+		private string exposuretime;		public string ExposureTime 	{get { return exposuretime; }	}
+		private string fnumber;			public string FNumber 		{get { return fnumber; }	}
+		private string shutterspeedvalue;	public string ShutterSpeedValue {get { return shutterspeedvalue; }	}
+		private string aperturevalue;		public string ApertureValue 	{get { return aperturevalue; }	}
+		private string exposurebiasvalue;	public string ExposureBiasValue {get { return exposurebiasvalue; }	}
+		private string maxaperturevalue;	public string MaxApertureValue	{get { return maxaperturevalue; }	}
+		private string meteringmode;		public string MeteringMode 	{get { return meteringmode; }	}
+		private string focallength;		public string FocalLength 	{get { return focallength; }	}
+		private string colorspace;		public string ColorSpace 	{get { return colorspace; }	}
+		private string pixelxdimension;		public string PixelXDimension 	{get { return pixelxdimension; }	}
+		private string pixelydimension;		public string PixelYDimension 	{get { return pixelydimension; }	}
+		private string exposuremode;		public string ExposureMode 	{get { return exposuremode; }	}
+		private string whitebalance;		public string WhiteBalance 	{get { return whitebalance; }	}
+		private string digitalzoomratio;	public string DigitalZoomRatio	{get { return digitalzoomratio; }	}
+		private string lightsource;		public string LightSource	{get { return lightsource; }	}
+		private string isospeed;		public string ISOSpeed		{get { return isospeed; }	}
+
+		// http://ns.adobe.com/exif/1.0
+		private string adobe_usercomment;	public string adobe_UserComment {get { return adobe_usercomment; }	}
+		
+		// http://purl.org/dc/elements/1.1/
+		private string creator;			public string Creator 		{get { return creator; }	}
+		private string rights;			public string Rights 		{get { return rights; }	}	
+		private string [] keywords;		public string [] Keywords 	{get { return keywords; } }
+		private string dc_description;		public string dc_Description 	{get { return dc_description; }}
+
+		// http://ns.adobe.com/xap/1.0/
+		// dc:title
+		private string dc_title;		public string dc_Title 	{get { return dc_title; }	}
+
+
+		// http://ns.iview-multimedia.com/mediapro/1.0/
+		// Iptc4xmpCore:Location
+		private string location;		public string Location 		{get { return location; }	}	
+	
+		// http://ns.adobe.com/xap/1.0
+		private string rating;			public string Rating		{get { return rating; }	}
+
+		// do not know
+		private string bitspersample;		public string BitsPerSample	{get { return bitspersample; }	}
+
+		public XmpTagsMetadata (string photo_path, string import_path)
+		{
+			this.xmptags_photofile = photo_path;
+			this.xmptags_sidecar_exist = false;
+			this.xmptags_found_xmp_tags = false;
+
+			// Check if we can find a sidecar file.
+			string tmpfile = String.Format ("{0}{1}{2}.xmp",
+						Path.GetDirectoryName (photo_path),
+						Path.DirectorySeparatorChar,
+						Path.GetFileName (photo_path));
+
+			if (File.Exists (tmpfile))
+				this.xmptags_sidecar_exist = true;
+			else {
+				// FIXME, When F-Spot copies the sidecar file with the photo, we do not need the extra path.
+				// FIXME bug #342892 http://bugzilla.gnome.org/show_bug.cgi?id=342892
+				tmpfile = String.Format ("{0}{1}{2}.xmp",
+							Path.GetDirectoryName (import_path),
+							Path.DirectorySeparatorChar,
+							Path.GetFileName (import_path));
+				if (File.Exists (tmpfile))
+					this.xmptags_sidecar_exist = true;
+			}
+			if (this.xmptags_sidecar_exist) {
+				this.xmptags_sidecarfile = tmpfile;
+				System.Console.WriteLine ("XMP Sidecar file found at {0}", this.xmptags_sidecarfile);
+			}
+		}
+		
+		private string TrimEmptyChars (string txt)
+		{
+			// This small function will ensure there are no null characters in a string.
+
+			string tmp;
+
+			// Return if already empty
+			if ( (txt == null) || (txt.Length == 0) )
+				return null;
+
+			// Clean normal blanks
+			tmp = txt.Trim();
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+
+			// Clean char 0
+			char[] emptychar = new char[1];
+			emptychar[0] = (char) 0;
+			tmp = tmp.Trim (emptychar);
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+
+			// clean normal blanks again
+			tmp = tmp.Trim();
+
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+			return tmp;
+		}
+
+		
+		private void GetSingleRowData (string title, string stmt_obj_str)
+		{
+			// This functions will put the stmt_obj_str to a variable based on the title string.
+			switch (title) {
+				// http://ns.adobe.com/photoshop/1.0
+				case "City" :
+					city = stmt_obj_str;
+					break;
+				case "State" :
+					state = stmt_obj_str;
+					break;
+				case "Country" :
+					country = stmt_obj_str;
+					break;
+				case "Headline" :
+					ps_headline = stmt_obj_str;
+					break;
+				case "Caption" :
+					ps_caption = stmt_obj_str;
+					break;
+				case "Urgency" :
+					urgency = stmt_obj_str;
+					break;
+				case "CaptionWriter" :
+					captionwriter = stmt_obj_str;
+					break;
+				case "Credit" :
+					credit = stmt_obj_str;
+					break;
+				case "Category" :
+					category = stmt_obj_str;
+					break;
+					
+				// http://ns.adobe.com/tiff/1.0
+				case "Make" :
+					make = stmt_obj_str;
+					break;
+				case "Model" :
+					model = stmt_obj_str;
+					break;
+				case "Orientation" :
+					orientation = stmt_obj_str;
+					break;
+				case "ResolutionUnit" :
+					resolutionunit = stmt_obj_str;
+					break;
+					
+				// http://ns.adobe.com/exif/1.0					
+				case "ExposureTime" :
+					exposuretime = stmt_obj_str;
+					break;
+				case "FNumber" :
+					fnumber = stmt_obj_str;
+					break;
+				case "ShutterSpeedValue" :
+					shutterspeedvalue = stmt_obj_str;
+					break;
+				case "ApertureValue" :
+					aperturevalue = stmt_obj_str;
+					break;
+				case "ExposureBiasValue" :
+					exposurebiasvalue = stmt_obj_str;
+					break;
+				case "MaxApertureValue" :
+					maxaperturevalue = stmt_obj_str;
+					break;
+				case "MeteringMode" :
+					meteringmode = stmt_obj_str;
+					break;
+				case "FocalLength" :
+					focallength= stmt_obj_str;
+					break;
+				case "ColorSpace" :
+					colorspace = stmt_obj_str;
+					break;
+				case "PixelXDimension" :
+					pixelxdimension = stmt_obj_str;
+					break;
+				case "PixelYDimension" :
+					pixelydimension = stmt_obj_str;
+					break;
+				case "ExposureMode" :
+					exposuremode = stmt_obj_str;
+					break;
+				case "WhiteBalance" :
+					whitebalance = stmt_obj_str;
+					break;
+				case "DigitalZoomRatio" :
+					digitalzoomratio = stmt_obj_str;
+					break;
+				case "LightSource" :
+					lightsource = stmt_obj_str;
+					break;
+					
+				// http://ns.iview-multimedia.com/mediapro/1.0/
+				// Iptc4xmpCore:Location
+				case "Location" :
+					location = stmt_obj_str;
+					break;
+
+				// http://ns.adobe.com/xap/1.0									
+				case "Rating" :
+					rating = stmt_obj_str;
+					break;
+					
+				default :
+#if enable_debug
+					Console.WriteLine ("====> Not handled importing XMP row TITLE >>{0}<<::>{1}<", title, stmt_obj_str);
+#endif
+					break;
+			}
+		}
+		
+		private void GetCollectionData (MemoryStore substore, string CollectionTitle)
+		{
+			// This function will collect all variables that are in a bag/collection.
+			// This is typically keywords, but also some other fields.
+			// This function will store the bag's variable into items_list, and if there are more items
+			// in the bag/collection, call itself recursively with the next item.
+			string type = null;
+
+			foreach (Statement stmt in substore) {
+				if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("rdf:type")) {
+					string prefix; // not used
+					MetadataStore.Namespaces.Normalize (stmt.Object.ToString (), out prefix, out type);
+				}
+			}
+
+			foreach (Statement sub in substore) {
+				if (sub.Object is Literal) {
+					string predicate = sub.Predicate.ToString ();
+					string title = System.IO.Path.GetFileName (predicate);
+					string value = ((Literal)(sub.Object)).Value;
+					
+					Description.GetDescription (substore, sub, out title, out value);
+#if enable_debug
+					Console.WriteLine ("8 type = >>{0}<<", type);
+					Console.WriteLine ("9 title = >>{0}<<", title);
+					Console.WriteLine ("10 value = >>{0}<<", value);
+#endif
+
+					if (type == null) {
+						// Nothing to collect
+					} else {
+						// Add this value to the collection (to be processed after we are 
+						// finished with this main collection.
+						items_list.Add(value);
+					}
+				} else {
+					if (type == null) {
+						MemoryStore substore2 = substore.Select (new Statement ((Entity)sub.Object, null, null, null));
+// FIXME, should it be substore or substore2 here?
+						if (substore.StatementCount > 0) {
+							// Since there are more bags/statements to collect.
+							// Lets call ourself recursively to collect next one.
+							GetCollectionData (substore2, CollectionTitle);
+						}
+					}
+				} // else
+			} // foreach
+		}
+		
+		private void ProcessItemsList (string title)
+		{
+			switch (title) {
+				// http://ns.adobe.com/photoshop/1.0/
+				case "SupplementalCategories" : // fall through to keyword case
+					goto case "Subject and Keywords";
+
+				// http://purl.org/dc/elements/1.1/
+				case "Subject and Keywords" : 
+					// Check first if we already have some keywords. If so
+					// then add the existing keywords to the items_list
+					if (keywords != null) { 
+						// we already have some keywords.
+						foreach (string keyword in keywords) {
+							items_list.Add(keyword); // add keyword to the list of keywords
+//							Console.WriteLine ("-----> Adding existing tag = >>{0}<<", keyword==null ? "null" : keyword );
+						}	
+					}
+					keywords = new string[items_list.Count]; 
+					items_list.CopyTo(keywords);
+					break;
+
+				// http://purl.org/dc/elements/1.1
+				case "Creator" :
+					if (items_list.Count > 0)
+						creator = items_list[0].ToString();
+					break;
+
+				// http://purl.org/dc/elements/1.1
+				case "Copyright" :
+					if (items_list.Count > 0)
+						rights = items_list[0].ToString();
+					break;
+				
+				// http://purl.org/dc/elements/1.1
+				case "Description" :
+					if (items_list.Count > 0)
+						dc_description = items_list[0].ToString();
+					break;
+					
+				// http://ns.adobe.com/xap/1.0/
+				case "Title" :
+					if (items_list.Count > 0)
+						dc_title = items_list[0].ToString();
+					break;
+					
+				// http://ns.adobe.com/exif/1.0
+				case "UserComment" :
+					if (items_list.Count > 0) {
+						adobe_usercomment = items_list[0].ToString();
+						// Get rid of weird char(0) and spaces and such.
+						adobe_usercomment = TrimEmptyChars(adobe_usercomment);
+					}
+					break;
+								
+				// http://ns.adobe.com/exif/1.0
+				case "ISOSpeedRatings" :
+					if (items_list.Count > 0)
+						isospeed = items_list[0].ToString();
+					break;
+					
+				// http://ns.adobe.com/exif/1.0
+				case "SubjectArea" :
+					break; // not handled
+					
+				// http://ns.adobe.com/exif/1.0
+				case "Components Configuration" : 
+					break; // not handled
+
+				// http://ns.adobe.com/exif/1.0
+				case "ExifCFAPattern" :
+					break;	// not handled
+					
+				// http://ns.adobe.com/tiff/1.0	
+				case "ReferenceBlackWhite" :
+					break;	// not handled
+					
+				// 
+				case "BitsPerSample" :
+					if (items_list.Count > 0)
+						bitspersample = items_list[0].ToString();
+					break;
+
+				// 	
+				case "ToneCurve" :
+					break;	// not handled
+					
+				// 
+				case "Flash" :
+					break;	// not handled
+					
+
+				default:
+					Console.WriteLine ("====> Not handled importing XMP Collection TITLE >>{0}<<", title);
+					if (keywords != null)
+						foreach (string keyword in keywords)
+							Console.WriteLine ("====> Missing tag = >>{0}<<", keyword==null ? "null" : keyword );
+					break;
+			}	
+		}
+	
+		public bool EmptyTags()
+		{
+			return ( (this.xmptags_found_xmp_tags || this.xmptags_found_sidecar_tag) == false);
+		}
+
+		public bool AnySidecarTags()
+		{
+			return ( this.xmptags_found_sidecar_tag);
+		}
+		
+		public void Read_Sidecar_tags()
+		{
+			bool tmp_bool = false;
+			if (xmptags_sidecar_exist)
+				tmp_bool = Read_tags ( true );
+			this.xmptags_found_sidecar_tag = tmp_bool;
+		}
+		
+		public void Read_Embedded_tags()
+		{
+			this.xmptags_found_xmp_tags = Read_tags ( false );
+		}
+
+		private bool Read_tags (bool read_sidecar_data)
+		{
+			XmpFile xmpfile = null;
+			MetadataStore store = new MetadataStore ();		
+
+			// Initialize variable
+			items_list = new System.Collections.ArrayList();
+			
+			try {
+				// First prepare the meta store, either from a sidecar of embedded 
+				if (read_sidecar_data) {
+#if enable_debug				
+					Console.WriteLine ("Importing sidecar metadata from sidecar {0}", xmptags_sidecarfile);
+#endif
+					xmpfile = new XmpFile(System.IO.File.OpenRead(xmptags_sidecarfile));
+					store = xmpfile.Store;
+				} else {
+#if enable_debug				
+					Console.WriteLine ("Importing embedded metadata from {0}", xmptags_photofile);	
+#endif
+					ImageFile img = ImageFile.Create (xmptags_photofile);
+
+					if (img is SemWeb.StatementSource) {
+						SemWeb.StatementSource source = (SemWeb.StatementSource)img;
+						source.Select (store);
+					}
+
+					// Get the EXIF description
+					if (img is FSpot.JpegFile) {
+						FSpot.JpegFile jimg = img as FSpot.JpegFile;			
+						exif_description = jimg.Description;
+//						Console.WriteLine ("Set the description to JPEG = >>{0}<<" ,jimg.Description);	
+					} else if (img is FSpot.Png.PngFile) {
+						FSpot.Png.PngFile png = img as FSpot.Png.PngFile;
+						exif_description = png.Description;
+//						Console.WriteLine ("Set the description to PNG = >>{0}<<" ,png.Description);	
+					}
+					exif_description = TrimEmptyChars (exif_description);
+
+				} // if xmptags_sidecar
+
+				// If there are any XMP tags, go through them one by one...
+				if (store.StatementCount > 0) {
+					foreach (SemWeb.Statement stmt in store) {				
+
+						// Skip anonymous subjects because they are
+						// probably part of a collection
+						if (stmt.Subject.Uri == null) 
+							continue;
+
+						string title;
+						string value;
+						title = null;
+						value = null;
+						
+						Description.GetDescription (store, stmt, out title, out value);
+
+						bool thisIsCollection = (value == null);
+#if enable_debug
+						Console.WriteLine ("1 Subject = >>{0}<<" ,stmt.Subject);	
+						Console.WriteLine ("2 Predicate = >>{0}<<", stmt.Predicate);
+						Console.WriteLine ("3 Object = >>{0}<<", stmt.Object);
+						Console.WriteLine ("4 Meta = >>{0}<<", stmt.Meta);
+						Console.WriteLine ("5 Uri = >>{0}<<", stmt.Subject.Uri);
+						Console.WriteLine ("6 Title = >>{0}<<", title);
+						Console.WriteLine ("7 Value = >>{0}<< --- Collection {1}", value == null ? "null" : value, value == null ? "YES" : "no" );
+#endif
+
+						string stmt_obj_str;
+						if (stmt.Object is SemWeb.Literal)
+							stmt_obj_str = ((SemWeb.Literal)(stmt.Object)).Value;
+						else
+							stmt_obj_str = stmt.Object.ToString();
+
+						if (thisIsCollection) {
+							MemoryStore substore = store.Select (new SemWeb.Statement ((Entity)stmt.Object, null, null, null));
+							
+							// Since start tag was found for this list, we clear it so we have a know starting state.
+							items_list.Clear();
+							
+							// Collect all the values stored in this collection/bag.
+							// It will put the result in the items_list array
+							GetCollectionData (substore, title);
+							
+							// Convert the items_list (from the collection/bag's values) to specific variables/keywords
+							ProcessItemsList (title);
+						} else {
+							// This XMP title just contains one value, and not a collection/bag. 
+							GetSingleRowData (title, stmt_obj_str);
+						}
+					} // foreach
+				} else 
+					return false; // No XMP tags found
+				
+
+			} catch (Exception e) {
+				Console.WriteLine ("Exception: {0}", e.ToString ());
+				return false;
+			}
+		
+#if enable_debug			
+			Console.WriteLine ("Have city = >>{0}<<", city==null ? "null" : city);
+			Console.WriteLine ("Have state = >>{0}<<", state==null ? "null" : state);
+			Console.WriteLine ("Have country = >>{0}<<", country==null ? "null" : country );
+			Console.WriteLine ("Have ps_headline = >>{0}<<", ps_headline==null ? "null" : ps_headline);
+			Console.WriteLine ("Have ps_caption = >>{0}<<", ps_caption==null ? "null" : ps_caption);
+			Console.WriteLine ("Have urgency = >>{0}<<", urgency==null ? "null" : urgency );
+			Console.WriteLine ("Have location = >>{0}<<", location==null ? "null" : location );	
+			Console.WriteLine ("Have dc_title = >>{0}<<", dc_title==null ? "null" : dc_title );	
+			Console.WriteLine ("Have dc_description = >>{0}<<", dc_description==null ? "null" : dc_description );	
+			Console.WriteLine ("Have exif description = >>{0}<<", exif_description==null ? "null" : exif_description );	
+			Console.WriteLine ("Have adobe usercomment = >>{0}<<", adobe_usercomment==null ? "null" : adobe_usercomment );	
+			Console.WriteLine ("Have rights = >>{0}<<", rights==null ? "null" : rights );	
+			Console.WriteLine ("Have creator = >>{0}<<", creator==null ? "null" : creator );	
+			Console.WriteLine ("Have credit = >>{0}<<", credit==null ? "null" : credit );	
+			Console.WriteLine ("Have category = >>{0}<<", category==null ? "null" : category );	
+			if (keywords == null)
+				Console.WriteLine ("Have keyword = >>null<<" );
+			else
+				foreach (string keyword in keywords) 
+					Console.WriteLine ("Have keyword = >>{0}<<", keyword==null ? "null" : keyword );	
+
+			Console.WriteLine ("Have make = >>{0}<<", make==null ? "null" : make );	
+			Console.WriteLine ("Have model = >>{0}<<", model==null ? "null" : model );	
+			Console.WriteLine ("Have orientation = >>{0}<<", orientation==null ? "null" : orientation );	
+			Console.WriteLine ("Have resolutionunit = >>{0}<<", resolutionunit==null ? "null" : resolutionunit );	
+			Console.WriteLine ("Have exposuretime = >>{0}<<", exposuretime==null ? "null" : exposuretime );	
+			Console.WriteLine ("Have fnumber = >>{0}<<", fnumber==null ? "null" : fnumber );	
+			Console.WriteLine ("Have shutterspeedvalue = >>{0}<<", shutterspeedvalue==null ? "null" : shutterspeedvalue );	
+			Console.WriteLine ("Have aperturevalue = >>{0}<<", aperturevalue==null ? "null" : aperturevalue );	
+			Console.WriteLine ("Have exposurebiasvalue = >>{0}<<", exposurebiasvalue==null ? "null" : exposurebiasvalue );	
+			Console.WriteLine ("Have maxaperturevalue = >>{0}<<", maxaperturevalue==null ? "null" : maxaperturevalue );	
+			Console.WriteLine ("Have meteringmode = >>{0}<<", meteringmode==null ? "null" : meteringmode );	
+			Console.WriteLine ("Have focallength = >>{0}<<", focallength==null ? "null" : focallength );	
+			Console.WriteLine ("Have colorspace = >>{0}<<", colorspace==null ? "null" : colorspace );	
+			Console.WriteLine ("Have pixelxdimension = >>{0}<<", pixelxdimension==null ? "null" : pixelxdimension );	
+			Console.WriteLine ("Have pixelydimension = >>{0}<<", pixelydimension==null ? "null" : pixelydimension );	
+			Console.WriteLine ("Have exposuremode = >>{0}<<", exposuremode==null ? "null" : exposuremode );	
+			Console.WriteLine ("Have whitebalance = >>{0}<<", whitebalance==null ? "null" : whitebalance );	
+			Console.WriteLine ("Have digitalzoomratio = >>{0}<<", digitalzoomratio==null ? "null" : digitalzoomratio );	
+			Console.WriteLine ("Have isospeed = >>{0}<<", isospeed==null ? "null" : isospeed );	
+			Console.WriteLine ("Have bitspersample = >>{0}<<", bitspersample==null ? "null" : bitspersample );	
+			Console.WriteLine ("Have captionwriter = >>{0}<<", captionwriter==null ? "null" : captionwriter );	
+			Console.WriteLine ("Have rating = >>{0}<<", rating==null ? "null" : rating );	
+	
+#endif
+//			Console.WriteLine ("Done importing xmp metadata");
+			return true;
+		}
+	}
+
+} // namespace
