f-spot r3541 - in trunk: . src src/Extensions src/Widgets



Author: sdelcroix
Date: Tue Jan 15 16:07:30 2008
New Revision: 3541
URL: http://svn.gnome.org/viewvc/f-spot?rev=3541&view=rev

Log:
Rating patch


Added:
   trunk/src/RatingFilter.cs
   trunk/src/RatingMenu.cs
   trunk/src/Widgets/Rating.cs
Modified:
   trunk/ChangeLog
   trunk/src/Extensions/PopupCommands.cs
   trunk/src/FSpot.addin.xml
   trunk/src/FileBrowsableItem.cs
   trunk/src/IBrowsableItem.cs
   trunk/src/MainWindow.cs
   trunk/src/Makefile.am
   trunk/src/MetadataStore.cs
   trunk/src/PhotoQuery.cs
   trunk/src/PhotoStore.cs
   trunk/src/PhotoView.cs
   trunk/src/Preferences.cs
   trunk/src/QueryWidget.cs
   trunk/src/SingleView.cs
   trunk/src/TimeAdaptor.cs
   trunk/src/Updater.cs
   trunk/src/Widgets/IconView.cs
   trunk/src/XmpTagsImporter.cs
   trunk/src/f-spot.glade

Modified: trunk/src/Extensions/PopupCommands.cs
==============================================================================
--- trunk/src/Extensions/PopupCommands.cs	(original)
+++ trunk/src/Extensions/PopupCommands.cs	Tue Jan 15 16:07:30 2008
@@ -104,4 +104,23 @@
 			MainWindow.Toplevel.HandleTagMenuActivate (o, e);
 		}
 	}
+
+	public class Rate : IMenuGenerator
+	{
+		private RatingMenu rating_menu;
+
+		public Gtk.Menu GetMenu ()
+		{
+			rating_menu = new RatingMenu (null);
+			rating_menu.RatingSelected += MainWindow.Toplevel.HandleRatingMenuSelected;
+			return (Gtk.Menu) rating_menu;
+		}
+
+		public void OnActivated (object o, EventArgs e)
+		{
+			if (rating_menu != null)
+				rating_menu.Populate ();
+		}
+
+	}
 }

Modified: trunk/src/FSpot.addin.xml
==============================================================================
--- trunk/src/FSpot.addin.xml	(original)
+++ trunk/src/FSpot.addin.xml	Tue Jan 15 16:07:30 2008
@@ -36,5 +36,6 @@
 		<MenuSeparator id = "Separator3" />
 		<MenuGenerator id = "AttachTag" _label = "_Attach Tag" icon = "gtk-add" generator_type = "FSpot.Extensions.AttachTag" />
 		<MenuGenerator id = "RemoveTag" _label = "Rem_ove Tag" icon = "gtk-remove" generator_type = "FSpot.Extensions.RemoveTag" />
+		<MenuGenerator id = "RatingMenu" _label = "Set ra_ting" generator_type = "FSpot.Extensions.Rate" />
 	</Extension>	
 </Addin>

Modified: trunk/src/FileBrowsableItem.cs
==============================================================================
--- trunk/src/FileBrowsableItem.cs	(original)
+++ trunk/src/FileBrowsableItem.cs	Tue Jan 15 16:07:30 2008
@@ -80,6 +80,12 @@
 			}
 		}
 
+		public uint Rating {
+			get {
+				return 0; //FIXME ndMaxxer: correct?
+			}
+		}
+
 		public void Dispose ()
 		{
 			img.Dispose ();

Modified: trunk/src/IBrowsableItem.cs
==============================================================================
--- trunk/src/IBrowsableItem.cs	(original)
+++ trunk/src/IBrowsableItem.cs	Tue Jan 15 16:07:30 2008
@@ -81,6 +81,10 @@
 		string Name {
 			get; 
 		}
+
+		uint Rating {
+			get; 
+		}
 	}
 
 

Modified: trunk/src/MainWindow.cs
==============================================================================
--- trunk/src/MainWindow.cs	(original)
+++ trunk/src/MainWindow.cs	Tue Jan 15 16:07:30 2008
@@ -79,6 +79,7 @@
 	[Glade.Widget] CheckMenuItem display_timeline;
 	[Glade.Widget] CheckMenuItem display_dates_menu_item;
 	[Glade.Widget] CheckMenuItem display_tags_menu_item;
+	[Glade.Widget] CheckMenuItem display_ratings_menu_item;
 
 	[Glade.Widget] MenuItem zoom_in;
 	[Glade.Widget] MenuItem zoom_out;
@@ -98,8 +99,10 @@
 	[Glade.Widget] MenuItem find_add_tag_with;
 	
 	[Glade.Widget] MenuItem clear_date_range;
+	[Glade.Widget] MenuItem clear_rating_filter;
 
 	[Glade.Widget] CheckMenuItem find_untagged;
+	[Glade.Widget] CheckMenuItem find_unrated;
 	
 	[Glade.Widget] MenuItem last_roll;
 	[Glade.Widget] MenuItem select_rolls;
@@ -364,6 +367,7 @@
 		LoadPreference (Preferences.ZOOM);
 		LoadPreference (Preferences.SHOW_TAGS);
 		LoadPreference (Preferences.SHOW_DATES);
+		LoadPreference (Preferences.SHOW_RATINGS);
 		icon_view_scrolled.Add (icon_view);
 		icon_view.DoubleClicked += HandleDoubleClicked;
 		icon_view.Vadjustment.ValueChanged += HandleIconViewScroll;
@@ -855,6 +859,7 @@
 		HigMessageDialog md = new HigMessageDialog (main_window, DialogFlags.DestroyWithParent, 
 							    MessageType.Error, ButtonsType.Ok, 
 							    short_msg, String.Format (long_msg, backup));
+		Console.WriteLine (e);
 		md.Run ();
 		md.Destroy ();
 	}
@@ -1365,6 +1370,27 @@
 	}	
 
 	//
+	// RatingMenu commands
+	//
+	
+	public void HandleRatingMenuSelected (int r) 
+	{
+		Photo p;
+		db.BeginTransaction ();
+		foreach (int num in SelectedIds ()) {
+			p = query.Photos [num];
+
+			if (r == -1)
+				p.RemoveRating();
+			else
+				p.Rating = (uint) r;
+			query.Commit (num);
+		}
+		db.CommitTransaction ();
+		this.photo_view.UpdateRating();
+	}
+
+	//
 	// TagMenu commands.
 	//
 
@@ -1770,6 +1796,7 @@
 		Preferences.Set (Preferences.SHOW_TIMELINE,		group_selector.Visible);
 		Preferences.Set (Preferences.SHOW_TAGS,			icon_view.DisplayTags);
 		Preferences.Set (Preferences.SHOW_DATES,		icon_view.DisplayDates);
+		Preferences.Set (Preferences.SHOW_RATINGS,		icon_view.DisplayRatings);
 
 		Preferences.Set (Preferences.GROUP_ADAPTOR,		(group_selector.Adaptor is DirectoryAdaptor) ? 1 : 0);
 		Preferences.Set (Preferences.GROUP_ADAPTOR_ORDER_ASC,   group_selector.Adaptor.OrderAscending);
@@ -2064,6 +2091,11 @@
 		icon_view.DisplayDates = display_dates_menu_item.Active;
 	}
 
+	void HandleDisplayRatings (object sender, EventArgs args)
+	{
+		icon_view.DisplayRatings = display_ratings_menu_item.Active;
+	}
+
 	void HandleDisplayGroupSelector (object sender, EventArgs args)
 	{
 		if (group_selector.Visible)
@@ -2172,9 +2204,11 @@
 	{
 		if (find_untagged.Active != query.Untagged)
 			find_untagged.Active = query.Untagged;
-
+		if (find_unrated.Active != query.Unrated)
+			find_unrated.Active = query.Unrated;
 		
 		clear_date_range.Sensitive = (query.Range != null);
+		clear_rating_filter.Sensitive = (query.RatingRange != null);
 		UpdateStatusLabel ();
 	}
 
@@ -2562,6 +2596,25 @@
 		query.RollSet = null;
 	}
 
+	void HandleSetRatingFilter (object sender, EventArgs args) {
+		query.Unrated = false;
+		find_unrated.Active = false;
+		RatingFilter.Set set_command = new RatingFilter.Set (query, main_window);
+		set_command.Execute ();
+	}
+
+	public void HandleClearRatingFilter (object sender, EventArgs args) {
+		query.RatingRange = null;
+		query.Unrated = false;
+		find_unrated.Active = false;
+	}
+
+	void HandleFindUnrated (object sender, EventArgs args) {
+		if (query.Unrated == find_unrated.Active)
+			return;
+
+		query.Unrated = !query.Unrated;
+	}	
 	
 	void HandleFindUntagged (object sender, EventArgs args) {
 		if (query.Untagged == find_untagged.Active)
@@ -2631,6 +2684,11 @@
 				//display_dates_menu_item.Toggle ();
 			break;
 		
+		case Preferences.SHOW_RATINGS:
+			if (display_ratings_menu_item.Active != (bool) val)
+				display_ratings_menu_item.Active = (bool) val;
+			break;
+		
 		case Preferences.GROUP_ADAPTOR:
 			if ((int) val == 1)
 				directory.Active = true;
@@ -2857,6 +2915,8 @@
 		sharpen.Sensitive = active_selection;
 		remove_from_catalog.Sensitive = active_selection;
 		
+		clear_rating_filter.Sensitive = (query.RatingRange != null);
+
 		last_roll.Sensitive = (db.Rolls.GetRolls (1).Length > 0);
 		select_rolls.Sensitive = (db.Rolls.GetRolls (2).Length > 1);
 		clear_roll_filter.Sensitive = (query.RollSet != null);

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Tue Jan 15 16:07:30 2008
@@ -175,6 +175,8 @@
 	$(srcdir)/PreviewPopup.cs 		\
 	$(srcdir)/PrintDialog.cs		\
 	$(srcdir)/ProgressDialog.cs		\
+	$(srcdir)/RatingFilter.cs		\
+	$(srcdir)/RatingMenu.cs			\
 	$(srcdir)/QueuedSqliteDatabase.cs	\
 	$(srcdir)/RepairDialog.cs		\
 	$(srcdir)/RotateCommand.cs		\
@@ -226,6 +228,7 @@
 	$(srcdir)/Widgets/PanZoom.cs		\
 	$(srcdir)/Widgets/Push.cs		\
 	$(srcdir)/Widgets/QueryView.cs		\
+	$(srcdir)/Widgets/Rating.cs		\
 	$(srcdir)/Widgets/Reveal.cs		\
 	$(srcdir)/Widgets/SaneTreeView.cs	\
 	$(srcdir)/Widgets/ScalingIconView.cs	\

Modified: trunk/src/MetadataStore.cs
==============================================================================
--- trunk/src/MetadataStore.cs	(original)
+++ trunk/src/MetadataStore.cs	Tue Jan 15 16:07:30 2008
@@ -246,6 +246,28 @@
 			Add (sink, FSpotXMPBase, predicate, type, values);
 		}
 
+		public void Delete (string predicate)
+		{
+			System.Collections.ArrayList to_remove = new System.Collections.ArrayList ();
+			foreach (Statement stmt in this) {
+				if (stmt.Predicate == MetadataStore.Namespaces.Resolve (predicate)) {
+					to_remove.Add (stmt);
+					break;
+				}
+			}
+
+			foreach (Statement stmt in to_remove)
+				this.Remove (stmt);
+		}
+
+		public void Update (string predicate, string value1)
+		{
+			// Delete first..
+			Delete (predicate);
+			// Add after...
+			AddLiteral (this, predicate, value1);
+		}
+
 		public void Update (string predicate, string type, string [] values)
 		{
 			Entity anon = null;
@@ -253,7 +275,8 @@
 			System.Collections.ArrayList to_remove = new System.Collections.ArrayList ();
 			foreach (Statement stmt in this) {
 				if (stmt.Predicate == MetadataStore.Namespaces.Resolve (predicate)) {
-					anon = (Entity) stmt.Object;
+					if (type != null) // only look further if we have a type.
+						anon = (Entity) stmt.Object;
 					to_remove.Add (stmt);
 					break;
 				}

Modified: trunk/src/PhotoQuery.cs
==============================================================================
--- trunk/src/PhotoQuery.cs	(original)
+++ trunk/src/PhotoQuery.cs	Tue Jan 15 16:07:30 2008
@@ -12,6 +12,7 @@
 		private string extra_condition;
 		private PhotoStore.DateRange range = null;
 		private RollSet roll_set = null;
+		private PhotoStore.RatingRange ratingrange = null;
 		
 		// Constructor
 		public PhotoQuery (PhotoStore store)
@@ -22,7 +23,7 @@
 			this.store.ItemsAddedOverDBus += delegate { RequestReload(); };
 			this.store.ItemsRemovedOverDBus += delegate { RequestReload(); };
 
-			photos = store.Query ((Tag [])null, null, range, roll_set);
+			photos = store.Query ((Tag [])null, null, range, roll_set, ratingrange);
 		}
 
 		public int Count {
@@ -134,12 +135,43 @@
  			}
 		}
 
+		public PhotoStore.RatingRange RatingRange {
+			get {
+				return ratingrange;
+			}
+			set {
+				if (value == ratingrange)
+					return;
+
+				ratingrange = value;
+				RequestReload ();
+			}
+		}
+
+		private bool unrated = false;
+		public bool Unrated {
+			get {
+				return unrated;
+			}
+			set {
+				if (value == unrated)
+					return;
+
+				unrated = value;
+				if (unrated)
+					ratingrange = new PhotoStore.RatingRange (PhotoStore.RatingRange.RatingType.Unrated);
+				else
+					ratingrange = null;
+				RequestReload ();
+			}
+		}
+		
 		public void RequestReload ()
 		{
 			if (untagged)
-				photos = store.QueryUntagged (range, roll_set);
+				photos = store.QueryUntagged (range, roll_set, ratingrange);
 			else
-				photos = store.Query (terms, extra_condition, range, roll_set);
+				photos = store.Query (terms, extra_condition, range, roll_set, ratingrange);
 
 			//this event will allow resorting the query content
 			if (PreChanged != null)

Modified: trunk/src/PhotoStore.cs
==============================================================================
--- trunk/src/PhotoStore.cs	(original)
+++ trunk/src/PhotoStore.cs	Tue Jan 15 16:07:30 2008
@@ -27,6 +27,14 @@
 
 using Banshee.Database;
 
+namespace FSpot{
+	public class NotRatedException : System.ApplicationException
+	{
+		public NotRatedException (string message) : base (message)
+		{}
+	}
+}
+
 public class PhotoVersion : FSpot.IBrowsableItem
 {
 	Photo photo;
@@ -77,6 +85,10 @@
 		get { return is_protected; }
 	}
 
+	public uint Rating {
+		get { return photo.Rating; }
+	}
+
 	public PhotoVersion (Photo photo, uint version_id, System.Uri uri, string name, bool is_protected)
 	{
 		this.photo = photo;
@@ -235,6 +247,29 @@
 		set { roll_id = value; }
 	}
 
+	private uint rating;
+	private bool rated = false;
+	public uint Rating {
+		get {
+			if (!rated)
+				throw new NotRatedException ("This photo is not rated yet");
+			else
+				return rating;
+		}
+		set {
+			if (value >= 0 && value <= 5) {
+				rating = value;
+				rated = true;
+			} else
+				rated = false;
+		}
+	}
+
+	public void RemoveRating ()
+	{
+		rated = false;
+	}
+
 	// Version management
 	public const int OriginalVersionId = 1;
 	private uint highest_version_id;
@@ -591,6 +626,14 @@
 			names [i] = tags [i].Name;
 		
 		xmp.Store.Update ("dc:subject", "rdf:Bag", names);
+		try { 
+			xmp.Store.Update ("xmp:Rating", (item as Photo).Rating.ToString());
+// FIXME - Should we also store/overwrite the Urgency field?
+//			uint urgency_value = (item as Photo).Rating + 1; // Urgency valid values 1 - 8
+//			xmp.Store.Update ("photoshop:Urgency", urgency_value.ToString());
+		} catch (NotRatedException) {
+			xmp.Store.Delete ("xmp:Rating");
+		}
 		xmp.Dump ();
 
 		return xmp;
@@ -667,6 +710,7 @@
 		time = DbUtils.DateTimeFromUnixTime (unix_time);
 
 		description = String.Empty;
+		rated = false;
 
 		// Note that the original version is never stored in the photo_versions table in the
 		// database.
@@ -784,7 +828,8 @@
 			"	uri		   STRING NOT NULL,		   " +
 			"	description        TEXT NOT NULL,	           " +
 			"	roll_id            INTEGER NOT NULL,		   " +
-			"	default_version_id INTEGER NOT NULL		   " +
+			"	default_version_id INTEGER NOT NULL,		   " +
+			"	rating		   INTEGER NULL			   " +
 			")");
 
 
@@ -833,13 +878,14 @@
 			string description = img.Description != null  ? img.Description.Split ('\0') [0] : String.Empty;
 	
 	 		uint id = (uint) Database.Execute (new DbCommand (
-				"INSERT INTO photos (time, uri, description, roll_id, default_version_id) "	+
-	 			"VALUES (:time, :uri, :description, :roll_id, :default_version_id)",
+				"INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating) "	+
+	 			"VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating)",
 	 			"time", unix_time,
 				"uri", new_uri.ToString (),
 	 			"description", description,
 				"roll_id", roll_id,
-	 			"default_version_id", Photo.OriginalVersionId));
+	 			"default_version_id", Photo.OriginalVersionId,
+	 			"rating", null));
 	
 			photo = new Photo (id, unix_time, new_uri);
 			AddToCache (photo);
@@ -969,7 +1015,7 @@
 		if (photo != null)
 			return photo;
 
-		SqliteDataReader reader = Database.Query(new DbCommand("SELECT time, uri, description, roll_id, default_version_id "
+		SqliteDataReader reader = Database.Query(new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating "
 			+ "FROM photos WHERE id = :id", "id", id));
 
 		if (reader.Read ()) {
@@ -980,6 +1026,10 @@
 			photo.Description = reader[2].ToString ();
 			photo.RollId = Convert.ToUInt32 (reader[3]);
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
+			if (reader [5] != null)
+				photo.Rating = Convert.ToUInt32 (reader [5]);
+			else
+				photo.RemoveRating();
 			AddToCache (photo);
 		}
 		reader.Close();
@@ -1003,7 +1053,7 @@
 	{
 		Photo photo = null;
 
-		SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id FROM photos "
+		SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating FROM photos "
                 + "WHERE uri = :uri", "uri", uri.ToString ()));
 
 		if (reader.Read ()) {
@@ -1014,6 +1064,10 @@
 			photo.Description = reader[2].ToString ();
 			photo.RollId = Convert.ToUInt32 (reader[3]);
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
+			if (reader [5] != null)
+				photo.Rating = Convert.ToUInt32 (reader [5]);
+			else
+				photo.RemoveRating();
 		}
 	        reader.Close();
 
@@ -1096,16 +1150,25 @@
 	private void Update (Photo photo) {
 		// Update photo.
 
+		uint rate = 0;
+		bool rated = false;
+		try {
+			rate = photo.Rating;
+			rated = true;
+		} catch {
+		}
 		Database.ExecuteNonQuery (new DbCommand (
 			"UPDATE photos SET description = :description, " + 
 			"default_version_id = :default_version_id, " + 
 			"time = :time, " + 
-			"uri = :uri " +
+			"uri = :uri, " +
+			"rating = :rating " +
 			"WHERE id = :id ",
 			"description", photo.Description,
 			"default_version_id", photo.DefaultVersionId,
 			"time", DbUtils.UnixTimeFromDateTime (photo.Time),
 			"uri", photo.VersionUri (Photo.OriginalVersionId).ToString (),
+			"rating", (rated ? String.Format ("{0}", rate) : null),
 			"id", photo.Id));
 
 		// Update tags.
@@ -1192,6 +1255,78 @@
 		 	ItemsRemovedOverDBus (this, new DbItemEventArgs (photos)); 
 	}
 
+	public class RatingRange 
+	{
+		public enum RatingType {
+			Unrated,
+			Rated
+		};
+
+		private RatingType ratetype;
+		public RatingType RateType {
+			get { 
+				return ratetype;
+			}
+			set { 
+				ratetype = value;
+			}
+		}
+
+		private uint minRating;		
+		public uint MinRating {
+			get {
+				return minRating;
+			}
+			set {
+				minRating = value;	
+			}
+		}
+
+		private uint maxRating;		
+		public uint MaxRating {
+			get {
+				return maxRating;
+			}
+			set {
+				maxRating = value;	
+			}
+		}
+
+		public RatingRange (RatingType ratetype) {
+			this.ratetype = ratetype;
+		}
+
+		public RatingRange (uint newRating)
+		{
+			this.ratetype = RatingType.Rated;
+			this.minRating = newRating;
+			this.maxRating = System.UInt32.MaxValue;
+		}
+
+		public RatingRange (uint newRating1, uint newRating2)
+		{
+			this.ratetype = RatingType.Rated;
+			this.minRating = newRating1;
+			this.maxRating = newRating2;
+		}
+
+		public string SqlClause ()
+		{
+			switch (this.ratetype) {
+			case (RatingType.Unrated) :
+				return String.Format (" photos.rating = NULL");
+				break;
+			case (RatingType.Rated) :
+				return String.Format (" photos.rating >= {0} AND photos.rating <= {1} ", minRating, maxRating);
+				break;
+			default :
+				return String.Empty;
+				break;
+			}
+		}
+
+	}
+
 	// Queries.
 
 	[Obsolete ("drop this, use IQueryCondition correctly instead")]
@@ -1202,26 +1337,29 @@
 
 		return  String.Format (" {0}{1}", added_where ? " AND " : " WHERE ", roll_set.SqlClause () );
 	}
-
-	
 	
-	public Photo [] Query (Tag [] tags, DateTime start, DateTime end, Roll [] rolls)
+	public Photo [] Query (Tag [] tags, DateTime start, DateTime end, Roll [] rolls, uint minRating, uint maxRating)
 	{
-		return Query (tags, null, new DateRange (start, end), new RollSet (rolls));
+		return Query (tags, null, new DateRange (start, end), new RollSet (rolls), new RatingRange (minRating, maxRating));
+	}
+
+	public Photo [] Query (Tag [] tags, DateTime start, DateTime end, Roll [] rolls, uint minRating)
+	{
+		return Query (tags, null, new DateRange (start, end), new RollSet (rolls), new RatingRange (minRating));
 	}
 
 	public Photo [] Query (Tag [] tags, Roll [] rolls)
 	{
-		return Query (tags, null, null, rolls == null ? null : new RollSet (rolls));
+		return Query (tags, null, null, rolls == null ? null : new RollSet (rolls), null);
 	}
 
 	public Photo [] Query (Tag [] tags, DateTime start, DateTime end)
 	{
-		return Query (tags, null, new DateRange (start, end), null);
+		return Query (tags, null, new DateRange (start, end), null, null);
 	}
 	
 	public Photo [] Query (Tag [] tags) {
-		return Query (tags, null, null, null);
+		return Query (tags, null, null, null, null);
 	}
 
 	public Photo [] Query (string query)
@@ -1242,6 +1380,10 @@
 				photo.Description = reader[3].ToString ();
 				photo.RollId = Convert.ToUInt32 (reader[4]);
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
+				if (reader [6] != null)
+					photo.Rating = Convert.ToUInt32 (reader [6]);
+				else
+					photo.RemoveRating ();
 				
 				version_list.Add (photo);
 			}
@@ -1278,7 +1420,8 @@
 				"photos.uri, "			+
 				"photos.description, "		+
 				"photos.roll_id, "		+
-				"photos.default_version_id "	+
+				"photos.default_version_id, "	+
+				"photos.rating "		+
 			"FROM photos " 				+
 			"WHERE uri LIKE \"file://{0}%\" "	+
 			"AND uri NOT LIKE \"file://{0}/%/%\"" , dir.FullName);
@@ -1288,6 +1431,11 @@
 
 	public Photo [] QueryUntagged (DateRange range, RollSet importidrange)
 	{
+		return QueryUntagged (range, importidrange, null);
+	}
+
+	public Photo [] QueryUntagged (DateRange range, RollSet importidrange, RatingRange ratingrange)
+	{
 		StringBuilder query_builder = new StringBuilder ();
 
 		query_builder.Append ("SELECT * FROM photos WHERE id NOT IN " +
@@ -1306,6 +1454,12 @@
 			added_where = true;
  		}
 
+		if (ratingrange != null) {
+			query_builder.Append (" AND");
+			query_builder.Append (ratingrange.SqlClause ());
+			added_where = true;
+ 		}
+
 		query_builder.Append("ORDER BY time");
 
 		return Query (query_builder.ToString ());
@@ -1313,10 +1467,15 @@
 
 	public Photo [] Query (Tag [] tags, string extra_condition, DateRange range, RollSet importidrange)
 	{
-		return Query (OrTerm.FromTags(tags), extra_condition, range, importidrange);
+		return Query (OrTerm.FromTags(tags), extra_condition, range, importidrange, null);
 	}
 
-	public Photo [] Query (Term searchexpression, string extra_condition, DateRange range, RollSet importidrange)
+	public Photo [] Query (Tag [] tags, string extra_condition, DateRange range, RollSet importidrange, RatingRange ratingrange)
+	{
+		return Query (OrTerm.FromTags(tags), extra_condition, range, importidrange, ratingrange);
+	}
+
+	public Photo [] Query (Term searchexpression, string extra_condition, DateRange range, RollSet importidrange, RatingRange ratingrange)
 	{
 		bool hide = (extra_condition == null);
 
@@ -1328,61 +1487,60 @@
 		//        photos.description,
 		//	  photos.roll_id,
 		//        photos.default_version_id
+		//        photos.rating
 		//                  FROM photos, photo_tags
-		//                  WHERE photos.id = photo_tags.photo_id
-		// 		                AND (photo_tags.tag_id = cat1tag1
-		//			            OR photo_tags.tag_id = cat1tag2 ) 
-		// 		                AND (photo_tags.tag_id = cat2tag1
-		//			            OR photo_tags.tag_id = cat2tag2 )
-		//			  	AND (photos.roll_id = roll_id1
-		//			   	    OR photos.roll_id = roll_id2 ...)
+		//		    WHERE photos.time >= time1 AND photos.time <= time2
+		//				AND photos.rating >= rat1 AND photos.rating <= rat2
+		//				AND photos.id NOT IN (select photo_id FROM photo_tags WHERE tag_id = HIDDEN)
+		//				AND photos.id IN (select photo_id FROM photo_tags where tag_id IN (tag1, tag2..)
+		//				AND extra_condition_string
 		//                  GROUP BY photos.id
 		
 		StringBuilder query_builder = new StringBuilder ();
+		ArrayList where_clauses = new ArrayList ();
 		query_builder.Append ("SELECT photos.id, " 			+
 					     "photos.time, "			+
 					     "photos.uri, "			+
 					     "photos.description, "		+
 				      	     "photos.roll_id, "   		+
-					     "photos.default_version_id "	+
+					     "photos.default_version_id, "	+
+					     "photos.rating "			+
 				      "FROM photos ");
 		
-		bool where_statement_added = false;
-
 		if (range != null) {
-			query_builder.Append (String.Format ("WHERE photos.time >= {0} AND photos.time <= {1} ",
-							     DbUtils.UnixTimeFromDateTime (range.Start), 
-							     DbUtils.UnixTimeFromDateTime (range.End)));
-			where_statement_added = true;
+			where_clauses.Add (String.Format ("photos.time >= {0} AND photos.time <= {1}",
+							  DbUtils.UnixTimeFromDateTime (range.Start), 
+							  DbUtils.UnixTimeFromDateTime (range.End)));
+
+		}
+
+		if (ratingrange != null) {
+			where_clauses.Add (ratingrange.SqlClause ());
 		}
 
 		if (importidrange != null) {
-			query_builder.Append (AddLastImportFilter (importidrange, where_statement_added));
-			where_statement_added = true;
+			where_clauses.Add (importidrange.SqlClause ());
 		}		
 		
 		if (hide && Core.Database.Tags.Hidden != null) {
-			query_builder.Append (String.Format ("{0} photos.id NOT IN (SELECT photo_id FROM photo_tags WHERE tag_id = {1}) ", 
-							     where_statement_added ? " AND " : " WHERE ", Core.Database.Tags.Hidden.Id));
-			where_statement_added = true;
+			where_clauses.Add (String.Format ("photos.id NOT IN (SELECT photo_id FROM photo_tags WHERE tag_id = {0})", 
+							  FSpot.Core.Database.Tags.Hidden.Id));
 		}
 		
 		if (searchexpression != null) {
-			query_builder.Append (String.Format ("{0} {1}", 
-							     where_statement_added ? " AND " : " WHERE ",
-							     searchexpression.SqlCondition()));
-			where_statement_added = true;
+			where_clauses.Add (searchexpression.SqlCondition ());
 		}
 
-		if (extra_condition != null && extra_condition.Length != 0) {
-			query_builder.Append (String.Format ("{0} {1} ",
-							     where_statement_added ? " AND " : " WHERE ",
-							     extra_condition));
-			where_statement_added = true;
+		if (extra_condition != null) {
+			where_clauses.Add (extra_condition);
 		}
 		
-		query_builder.Append ("ORDER BY photos.time");
-		Console.WriteLine("Query: {0}", query_builder.ToString());
+		if (where_clauses.Count > 0) {
+			query_builder.Append (" WHERE ");
+			query_builder.Append (String.Join (" AND ", ((String []) where_clauses.ToArray (typeof(String)))));
+		}
+		query_builder.Append (" ORDER BY photos.time");
+		Console.WriteLine ("Query: {0}", query_builder.ToString());
 		return Query (query_builder.ToString ());
 	}
 

Modified: trunk/src/PhotoView.cs
==============================================================================
--- trunk/src/PhotoView.cs	(original)
+++ trunk/src/PhotoView.cs	Tue Jan 15 16:07:30 2008
@@ -8,7 +8,7 @@
 
 namespace FSpot {
 public class PhotoView : EventBox {
-	FSpot.Delay description_delay; 
+	FSpot.Delay commit_delay; 
 
 	private bool has_selection = false;
 	private FSpot.PhotoImageView photo_view;
@@ -20,6 +20,7 @@
 	private Gtk.ToolButton display_next_button, display_previous_button;
 	private Label count_label;
 	private Entry description_entry;
+	private Widgets.Rating rating;
 
 	private Gtk.ToolButton crop_button;
 	private Gtk.ToolButton redeye_button;
@@ -195,6 +196,18 @@
 		description_entry.Changed += HandleDescriptionChanged;
 	}    
 
+	public void UpdateRating ()
+	{
+		rating.Changed -= HandleRatingChanged;
+		if (Item.IsValid)
+			try {
+				rating.Value = (int)Item.Current.Rating;
+			} catch (FSpot.NotRatedException) {
+				rating.Value = -1;
+			}
+		rating.Changed += HandleRatingChanged;
+	}
+
 	private void Update ()
 	{
 		if (UpdateStarted != null)
@@ -203,6 +216,7 @@
 		UpdateButtonSensitivity ();
 		UpdateCountLabel ();
 		UpdateDescriptionEntry ();
+		UpdateRating ();
 
 		if (UpdateFinished != null)
 			UpdateFinished (this);
@@ -368,12 +382,12 @@
 		ColorDialog.CreateForView (photo_view);
 	}	
 
-	int description_photo;
+	int changed_photo;
 	private bool CommitPendingChanges ()
 	{
-		if (description_delay.IsPending) {
-			description_delay.Stop ();
-			((PhotoQuery)query).Commit (description_photo);
+		if (commit_delay.IsPending) {
+			commit_delay.Stop ();
+			((PhotoQuery)query).Commit (changed_photo);
 		}
 		return true;
 	}
@@ -385,17 +399,36 @@
 		
 		((Photo)Item.Current).Description = description_entry.Text;
 		
-		if (description_delay.IsPending)
-			if (description_photo == Item.Index)
-				description_delay.Stop ();
+		if (commit_delay.IsPending)
+			if (changed_photo == Item.Index)
+				commit_delay.Stop ();
 			else
 				CommitPendingChanges ();
 		
 		tips.SetTip (description_entry, description_entry.Text, "This is a tip");
-		description_photo = Item.Index;
-		description_delay.Start ();
+		changed_photo = Item.Index;
+		commit_delay.Start ();
 	}
 
+	private void HandleRatingChanged (object o, EventArgs e)
+	{
+		if (!Item.IsValid)
+			return;
+
+		if ((o as Widgets.Rating).Value < 0)
+			((Photo)Item.Current).RemoveRating();
+		else
+			((Photo)Item.Current).Rating = (uint)(o as Widgets.Rating).Value;
+
+		if (commit_delay.IsPending)
+			if (changed_photo == Item.Index)
+				commit_delay.Stop();
+			else
+				CommitPendingChanges ();
+		changed_photo = Item.Index;
+		commit_delay.Start ();
+ 	}
+
 	private void HandlePhotoChanged (FSpot.PhotoImageView view)
 	{
 		if (query is PhotoQuery) {
@@ -431,7 +464,7 @@
 	{
 		this.query = query;
 
-		description_delay = new FSpot.Delay (1000, new GLib.IdleHandler (CommitPendingChanges));
+		commit_delay = new FSpot.Delay (1000, new GLib.IdleHandler (CommitPendingChanges));
 		this.Destroyed += HandleDestroy;
 
 		Name = "ImageContainer";
@@ -474,6 +507,10 @@
 		description_entry = new Entry ();
 		inner_hbox.PackStart (description_entry, true, true, 0);
 		description_entry.Changed += HandleDescriptionChanged;
+
+		rating = new Widgets.Rating();
+		inner_hbox.PackStart (rating, false, false, 0);
+		rating.Changed += HandleRatingChanged;
 		
 		inner_vbox.PackStart (inner_hbox, false, true, 0);
 

Modified: trunk/src/Preferences.cs
==============================================================================
--- trunk/src/Preferences.cs	(original)
+++ trunk/src/Preferences.cs	Tue Jan 15 16:07:30 2008
@@ -33,6 +33,7 @@
 		public const string SHOW_TAGS = "/apps/f-spot/ui/show_tags";
 		public const string SHOW_DATES = "/apps/f-spot/ui/show_dates";
 		public const string EXPANDED_TAGS = "/apps/f-spot/ui/expanded_tags";
+		public const string SHOW_RATINGS = "/apps/f-spot/ui/show_ratings";
 		public const string TAG_ICON_SIZE = "/apps/f-spot/ui/tag_icon_size";
 		
 		public const string GLASS_POSITION = "/apps/f-spot/ui/glass_position";
@@ -161,6 +162,7 @@
 			case SHOW_TIMELINE:
 			case SHOW_TAGS:
 			case SHOW_DATES:
+			case SHOW_RATINGS:
 			case VIEWER_SHOW_FILENAMES:
 			case EXPORT_GALLERY_ROTATE:
 			case EXPORT_PICASAWEB_SCALE:

Modified: trunk/src/QueryWidget.cs
==============================================================================
--- trunk/src/QueryWidget.cs	(original)
+++ trunk/src/QueryWidget.cs	Tue Jan 15 16:07:30 2008
@@ -8,10 +8,13 @@
 	public class QueryWidget : HighlightedBox {
 		PhotoQuery query;
 		LogicWidget logic_widget;
-        Gtk.HBox box;
+	        Gtk.HBox box;
 		Gtk.Label label;
 		Gtk.Label untagged;
-		Gtk.Label comma_label;
+		Gtk.Label unrated;
+		Gtk.Label rated;
+		Gtk.Label comma1_label;
+		Gtk.Label comma2_label;
 		Gtk.Label rollfilter;
 		Gtk.HBox warning_box;
 		Gtk.Button clear_button;
@@ -25,9 +28,9 @@
 
 		public QueryWidget (PhotoQuery query, Db db, TagSelectionWidget selector) : base(new HBox())
 		{
-            box = Child as HBox;
+			box = Child as HBox;
 			box.Spacing = 6;
-            box.BorderWidth = 2;
+			box.BorderWidth = 2;
 
 			tips.Enable ();
 
@@ -43,9 +46,21 @@
 			untagged.Visible = false;
 			box.PackStart (untagged, false, false, 0);
 
-			comma_label = new Gtk.Label (", ");
-			comma_label.Visible = false;
-			box.PackStart (comma_label, false, false, 0);
+			comma1_label = new Gtk.Label (", ");
+			comma1_label.Visible = false;
+			box.PackStart (comma1_label, false, false, 0);
+
+			unrated = new Gtk.Label (Catalog.GetString ("Unrated photos"));
+			unrated.Visible = false;
+			box.PackStart (unrated, false, false, 0);
+
+			rated = new Gtk.Label (Catalog.GetString ("Rated photos"));
+			rated.Visible = false;
+			box.PackStart (rated, false, false, 0);
+
+			comma2_label = new Gtk.Label (", ");
+			comma2_label.Visible = false;
+			box.PackStart (comma2_label, false, false, 0);
 
 			rollfilter = new Gtk.Label (Catalog.GetString ("Import roll"));	
 			rollfilter.Visible = false;
@@ -82,7 +97,7 @@
 		
 		public void HandleClearButtonClicked (object sender, System.EventArgs args)
 		{
-            Close ();
+			Close ();
 		}
 
 		public void Close ()
@@ -93,6 +108,8 @@
 			if (query.Untagged)
 				return;
 
+			query.Unrated = false;
+			query.RatingRange = null;
 			logic_widget.Clear = true;
 			logic_widget.UpdateQuery ();
 		}
@@ -114,16 +131,20 @@
 			if (query.ExtraCondition == null)
 				logic_widget.Clear = true;
 
-			if (!logic_widget.Clear || query.Untagged || (query.RollSet != null)) {
-                ShowBar ();
+			if (!logic_widget.Clear || query.Untagged || (query.RollSet != null) || query.Unrated || (query.RatingRange != null)) {
+		                ShowBar ();
 			} else {
 				HideBar ();
 			}
 
 			untagged.Visible = query.Untagged;
+			unrated.Visible = query.Unrated;
+			rated.Visible = (query.RatingRange != null) && !query.Unrated;
 			warning_box.Visible = (query.Count < 1);
-			comma_label.Visible = query.Untagged && (query.RollSet != null);
 			rollfilter.Visible = (query.RollSet != null);
+			comma1_label.Visible = (untagged.Visible && (unrated.Visible || rated.Visible));
+			comma2_label.Visible = (!untagged.Visible && (unrated.Visible || rated.Visible) && rollfilter.Visible) || 
+					       (untagged.Visible && rollfilter.Visible);
 
 		}
 

Added: trunk/src/RatingFilter.cs
==============================================================================
--- (empty file)
+++ trunk/src/RatingFilter.cs	Tue Jan 15 16:07:30 2008
@@ -0,0 +1,54 @@
+/*
+ * Rating.cs
+ *
+ * Author[s]
+ *    Bengt Thuree <bengt thuree com>
+ *
+ */
+
+using Gtk;
+using Gnome;
+
+public class RatingFilter {
+	public class Set : FSpot.GladeDialog {
+		FSpot.PhotoQuery query;
+		Gtk.Window parent_window;
+
+		[Glade.Widget] private Button ok_button;
+		[Glade.Widget] private SpinButton minrating;
+		[Glade.Widget] private SpinButton maxrating;
+
+		public Set (FSpot.PhotoQuery query, Gtk.Window parent_window)
+		{
+			this.query = query;
+			this.parent_window = parent_window;
+		}
+
+		public bool Execute ()
+		{
+			this.CreateDialog ("set_rating_filter");
+			
+			if (query.RatingRange != null) {
+				minrating.Value = query.RatingRange.MinRating;
+				maxrating.Value = query.RatingRange.MaxRating;
+			}
+
+			Dialog.TransientFor = parent_window;
+			Dialog.DefaultResponse = ResponseType.Ok;
+
+			ResponseType response = (ResponseType) this.Dialog.Run ();
+
+			bool success = false;
+
+			if (response == ResponseType.Ok) {
+				query.RatingRange = new PhotoStore.RatingRange ((uint) minrating.Value, (uint) maxrating.Value);
+				success = true;
+			}
+			
+			this.Dialog.Destroy ();
+			return success;
+		}
+	}
+}
+
+

Added: trunk/src/RatingMenu.cs
==============================================================================
--- (empty file)
+++ trunk/src/RatingMenu.cs	Tue Jan 15 16:07:30 2008
@@ -0,0 +1,93 @@
+/*
+ * RatingMenu.cs
+ *
+ * Author(s)
+ * 	Bengt Thuree  <bengt thuree com>
+ * 	Stephane Delcroix  <stephane delcroix org>
+ * 	
+ * See COPYING for licence terms
+ *
+ */
+
+using Gtk;
+using System;
+
+public class RatingMenu : Menu {
+	private MenuItem parent_item;
+
+	public delegate void RatingSelectedHandler (int r);
+	public event RatingSelectedHandler RatingSelected;
+	
+	public class RatingMenuItem : Gtk.MenuItem {
+		private int rating;
+
+		public int Rating {
+			get { return rating; }
+			set { rating = value; }
+		}
+
+		public RatingMenuItem (string label): base (label)
+		{
+			Rating = -1;
+		}
+
+		public RatingMenuItem (int r)
+		{
+			Rating = r;
+			if (r >= 0) {
+				FSpot.Widgets.RatingSmall rating_small = new FSpot.Widgets.RatingSmall(r,false);
+				Add(rating_small);
+			} else {
+				Label unrated = new Label(Mono.Unix.Catalog.GetString("Not rated"));
+				Add(unrated);
+			}
+		}
+	}
+
+	public RatingMenu (MenuItem item) 
+	{
+		if (item != null) {
+			item.Submenu = this;
+			item.Activated += HandlePopulate;
+			parent_item = item;
+		}
+	}
+
+	protected RatingMenu (IntPtr raw) : base (raw) {}
+
+	private void HandlePopulate (object obj, EventArgs args)
+	{
+		Populate(this);
+	}
+
+	bool populated = false;
+
+        public void Populate ()
+	{
+		Populate (this);
+	}
+
+        public void Populate (Gtk.Menu parent)
+	{
+		if (!populated) {
+			for (int k = -1; k <= 5; k ++) {
+				RatingMenuItem item = new RatingMenuItem (k);
+				parent.Append (item);
+				item.ShowAll ();
+				item.Activated += HandleActivate;
+			}
+			populated = true;
+		}
+	}
+	
+	void HandleActivate (object obj, EventArgs args)
+	{
+		if (RatingSelected != null) {
+			RatingMenuItem t = obj as RatingMenuItem;
+			if (t != null)
+				RatingSelected (t.Rating);
+			else 
+				Console.WriteLine ("Item was not rating usable");
+		}
+	}
+}

Modified: trunk/src/SingleView.cs
==============================================================================
--- trunk/src/SingleView.cs	(original)
+++ trunk/src/SingleView.cs	Tue Jan 15 16:07:30 2008
@@ -104,6 +104,7 @@
 					DragAction.Copy | DragAction.Move); 
 			directory_view.DisplayTags = false;
 			directory_view.DisplayDates = false;
+			directory_view.DisplayRatings = false;
 			directory_scrolled.Add (directory_view);
 
 			ThumbnailGenerator.Default.OnPixbufLoaded += delegate { directory_view.QueueDraw (); };

Modified: trunk/src/TimeAdaptor.cs
==============================================================================
--- trunk/src/TimeAdaptor.cs	(original)
+++ trunk/src/TimeAdaptor.cs	Tue Jan 15 16:07:30 2008
@@ -195,7 +195,7 @@
 		{
 			years.Clear ();
 
-			Photo [] photos = query.Store.Query ((Tag [])null, null, null, null);
+			Photo [] photos = query.Store.Query ((Tag [])null, null, null, null, null);
 			Array.Sort (query.Photos);
 			Array.Sort (photos);
 

Modified: trunk/src/Updater.cs
==============================================================================
--- trunk/src/Updater.cs	(original)
+++ trunk/src/Updater.cs	Tue Jan 15 16:07:30 2008
@@ -210,7 +210,27 @@
  					"FROM  {0} ", tmp_photos));
  			}, false);
  
-			// Update to version 11.0
+			// Update to version 11.0, rating
+			AddUpdate (new Version (11,0),delegate () {
+ 				string tmp_photos = MoveTableToTemp ("photos");
+ 				ExecuteNonQuery (
+ 					"CREATE TABLE photos (                                     " +
+ 					"	id                 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
+ 					"	time               INTEGER NOT NULL,	   	   " +
+ 					"	uri		   STRING NOT NULL,		   " +
+ 					"	description        TEXT NOT NULL,	           " +
+ 					"	roll_id            INTEGER NOT NULL,		   " +
+ 					"	default_version_id INTEGER NOT NULL,		   " +
+					"       rating             INTEGER NULL			   " +
+ 					")");
+ 
+ 				ExecuteNonQuery (String.Format (
+ 					"INSERT INTO photos (id, time, uri, description, roll_id, default_version_id, rating) " +
+ 					"SELECT id, time, uri, description, roll_id, default_version_id, null  " + 
+ 					"FROM  {0} ", tmp_photos));
+			});
+			
+			// Update to version 12.0
 			//AddUpdate (new Version (0,0),delegate () {
 			//	do update here
 			//});

Modified: trunk/src/Widgets/IconView.cs
==============================================================================
--- trunk/src/Widgets/IconView.cs	(original)
+++ trunk/src/Widgets/IconView.cs	Tue Jan 15 16:07:30 2008
@@ -124,6 +124,21 @@
 				}
 			}
 		}
+
+		private bool display_ratings = true;
+		public bool DisplayRatings {
+			get {
+				if (cell_width > 100)
+					return display_ratings;
+				else
+					return false;
+			}
+			
+			set {
+				display_ratings  = value;
+				QueueResize ();
+			}
+		}
 	
 		// Size of the frame around the thumbnail.
 		protected int cell_border_width = 10;
@@ -865,6 +880,16 @@
 				thumbnail.Dispose ();
 			}
 			Gdk.Rectangle layout_bounds = Gdk.Rectangle.Zero;
+			if (DisplayRatings) {
+				FSpot.Widgets.RatingSmall rating;
+				try {
+					rating = new FSpot.Widgets.RatingSmall ((int) photo.Rating, false);
+				} catch (NotRatedException) {
+					rating = new FSpot.Widgets.RatingSmall (-1, false);
+				}
+				rating.DisplayPixbuf.RenderToDrawable (BinWindow, Style.WhiteGC,
+						0, 0, region.X, region.Y, -1, -1, RgbDither.None, 0, 0);
+			}
 			if (DisplayDates) {
 				string date;
 				if (cell_width > 200) {

Added: trunk/src/Widgets/Rating.cs
==============================================================================
--- (empty file)
+++ trunk/src/Widgets/Rating.cs	Tue Jan 15 16:07:30 2008
@@ -0,0 +1,397 @@
+/*
+ * Rating.cs
+ *
+ * Author[s]
+ *    Gabriel Burt (original widget in Banshee)
+ *    Cosme Sevestre (original porting to F-Spot)
+ *    Stephane Delcroix
+ *
+ * Copyright (C) 2006 by the respective authors.
+ *
+ * This is free software, see COPYING for details
+ *
+ */
+
+using Gtk;
+using Gdk;
+using System;
+
+namespace FSpot.Widgets
+{
+	public class Rating : Gtk.EventBox
+	{
+		int rating;
+		Pixbuf display_pixbuf;
+		public object RatedObject;
+		bool mouse_over;
+		bool editable;
+
+		protected static int max_rating = 5;
+		protected static int min_rating = 1;
+		static Pixbuf icon_rated;
+		static Pixbuf icon_blank;
+		static Pixbuf icon_throw;
+		static Pixbuf icon_throwed;
+		static Pixbuf icon_unrated;
+
+		public event EventHandler Changed;
+		
+		public Rating () : this (-1, true) {} //Default value is NotRated, editable
+		public Rating (bool editable) : this (-1, editable) {}
+		public Rating (int rating) : this (rating, true) {} 
+
+		public Rating (int rating, bool editable)
+		{
+			this.rating = rating;
+			this.editable = editable;
+			
+			MouseOver = false;
+			EnterNotifyEvent += HandleMouseEnter;
+			LeaveNotifyEvent += HandleMouseLeave;
+			
+			CanFocus = true;
+			
+			display_pixbuf = new Pixbuf (Gdk.Colorspace.Rgb, true, 8, Width, Height);
+			
+			// Start display transparent
+			display_pixbuf.Fill (0xffffff00);
+			
+			DrawRating (DisplayPixbuf, Value);
+			
+			// DirectionChanged
+			Add (new Gtk.Image (display_pixbuf));
+			
+			ShowAll ();
+		}
+		
+		~Rating ()
+		{
+			display_pixbuf.Dispose ();
+			display_pixbuf = null;
+			
+			icon_rated = null;
+			icon_blank = null;
+		}
+		
+		public Pixbuf DrawRating (int val)
+		{
+			Pixbuf buf = new Pixbuf (Gdk.Colorspace.Rgb, true, 8, Width, Height);
+			DrawRating (buf, val);
+			return buf;
+		}
+		
+		public virtual void DrawRating (Pixbuf pbuf, int val)
+		{
+			// Clean pixbuf
+			pbuf.Fill (0xffffff00);
+			
+			if (val == -1 || (mouse_over && val != 0)) //NotRated or MouseOver
+				IconThrow.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+						pbuf, 0, 0);
+			if (val == 0) //Throwed
+				IconThrowed.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+						pbuf, 0, 0);
+			//Stars
+			for (int i = 0; i < MaxRating; i ++)
+				if (i <= val - MinRating)
+					IconRated.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+							pbuf, (i + 1) * IconRated.Width, 0);
+				else {
+					if (!mouse_over && val != -1)
+						continue;
+					IconNotRated.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+							pbuf, (i + 1) * IconRated.Width, 0);
+				}
+			//Unrate button
+			IconUnrated.CopyArea (0, 0, IconUnrated.Width, IconUnrated.Height,
+							pbuf, (max_rating - min_rating + 2) * IconUnrated.Width, 0); 
+		}
+		
+		private int RatingFromPosition (double x)
+		{
+			//System.Console.WriteLine ("Rating from position >>{0}<<", (int) (x / (double)(IconRated.Width)));
+			int pos = (int) (x / (double) IconRated.Width);
+			
+			if (pos == NumLevels - 1)
+				return -1;
+			else
+				return pos;
+		}
+		
+		private void HandleMouseEnter (object sender, EventArgs args)
+		{
+			mouse_over = true;
+			Redraw ();
+		}
+		
+		private void HandleMouseLeave (object sender, EventArgs args)
+		{
+			mouse_over = false;
+			Redraw ();
+		}
+		
+		// Event Handlers
+		[GLib.ConnectBefore]
+		protected override bool OnButtonPressEvent (Gdk.EventButton eb)
+		{
+			if (editable) {
+				if (eb.Button != 1)
+					return false;
+			
+				Value = RatingFromPosition (eb.X);
+			}
+			return true;
+		}
+		
+		public bool HandleKeyPress (Gdk.EventKey ek)
+		{
+			return this.OnKeyPressEvent (ek);
+		}
+		
+		[GLib.ConnectBefore]
+		protected override bool OnKeyPressEvent (Gdk.EventKey ek)
+		{
+			if (editable) {
+				switch (ek.Key) {
+				case Gdk.Key.Up:
+				case Gdk.Key.Right:
+				case Gdk.Key.plus:
+				case Gdk.Key.equal:
+					Value ++;
+					return true;
+					
+				case Gdk.Key.Down:
+				case Gdk.Key.Left:
+				case Gdk.Key.minus:
+					Value --;
+					return true;
+				}
+				
+				if (ek.KeyValue >= (48 + MinRating) &&
+				       ek.KeyValue <= (48 + MaxRating) &&
+				       ek.KeyValue <= 59) {
+					Value = (int) ek.KeyValue - 48;
+					return true;
+				}
+				
+				return false;
+			} else
+				return true;
+		}
+		
+		[GLib.ConnectBefore]
+		protected override bool OnScrollEvent (EventScroll args)
+		{
+			if (editable) {
+				switch (args.Direction) {
+				case Gdk.ScrollDirection.Up:
+				case Gdk.ScrollDirection.Right:
+					Value ++;
+					return true;
+					
+				case Gdk.ScrollDirection.Down:
+				case Gdk.ScrollDirection.Left:
+					Value --;
+					return true;
+				}
+				
+				return false;
+			} else
+				return true;
+		}
+		
+		[GLib.ConnectBefore]
+		protected override bool OnMotionNotifyEvent (Gdk.EventMotion evnt)
+		{
+			if (editable) {
+				System.Console.WriteLine ("OnMotionNotifyEvent");
+				// TODO draw highlights onmouseover a rating? (and clear on leaveNotify)
+				if (evnt.State != Gdk.ModifierType.Button1Mask)
+					return false;
+			
+				Value = RatingFromPosition (evnt.X);
+			}
+			return true;
+		}
+		
+		[GLib.ConnectBefore]
+		protected override bool OnFocusInEvent (Gdk.EventFocus evnt)
+		{
+			System.Console.WriteLine ("OnFocusInEvent");
+			return true;
+		}
+		
+		[GLib.ConnectBefore]
+		protected override bool OnFocused (DirectionType direction)
+		{
+			System.Console.WriteLine ("OnFocus");
+			return true;
+		}
+
+		private void Redraw()
+		{
+			DrawRating (DisplayPixbuf, Value);
+			QueueDraw ();
+		}
+
+		// Event Changed Dispatcher
+		private void OnChanged ()
+		{
+			Redraw();
+			EventHandler changed = Changed;
+			
+			if (changed != null)
+				changed (this, new EventArgs ());
+		}
+		
+		// Properties
+		public int Value {
+			get { return rating; }
+			
+			set {
+				// Same rating
+				if (rating == value)
+					return;
+				// Remove.trash.1-5 rating
+				if (value >= -1 && value <= max_rating) {
+					rating = value;
+					OnChanged ();
+				}
+			}
+		}
+		
+		public Pixbuf DisplayPixbuf {
+			get { return display_pixbuf; }
+		}
+		
+		public bool MouseOver {
+			get { return mouse_over; }
+			set { mouse_over = value; }
+		}
+		
+		public int MaxRating {
+			get { return max_rating; }
+		}
+		
+		public int MinRating {
+			get { return min_rating; }
+		}
+		
+		public virtual int NumLevels {
+			get { return max_rating - min_rating + 3; }
+		}
+		
+		public Pixbuf IconUnrated {
+			get {
+				if (icon_unrated == null)
+					icon_unrated = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-unrated", 16, (Gtk.IconLookupFlags)0);
+
+				return icon_unrated;
+			}
+
+			set { icon_unrated = value; }
+		}
+
+		public virtual Pixbuf IconRated {
+			get {
+				if (icon_rated == null)
+					icon_rated = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-rated", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_rated;
+			}
+			
+			set { icon_rated = value; }
+		}
+		
+		public virtual Pixbuf IconNotRated {
+			get {
+				if (icon_blank == null)
+					icon_blank = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-blank", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_blank;
+			}
+			
+			set { icon_blank = value; }
+		}
+		
+		public virtual Pixbuf IconThrow {
+			get {
+				if (icon_throw == null)
+					icon_throw = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-junk", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_throw;
+			}
+			
+			set { icon_throw = value; }
+		}
+		
+		public virtual Pixbuf IconThrowed {
+			get {
+				if (icon_throwed == null)
+					icon_throwed = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-junk-bold", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_throwed;
+			}
+			
+			set { icon_throw = value; }
+		}
+		
+		public virtual int Width {
+			get { return IconRated.Width * NumLevels; }
+		}
+		
+		public virtual int Height {
+			get { return IconRated.Height; }
+		}
+	}
+
+	public class RatingSmall : Rating
+	{
+		static Pixbuf icon_rated_small;
+		static Pixbuf icon_blank_small;
+		
+		public RatingSmall () : base (-1) {}
+		public RatingSmall (int rating) : base (rating) {}
+		public RatingSmall (bool editable) : base (editable) {}
+		public RatingSmall (int rating, bool editable) : base (rating, editable) {}
+
+		public override void DrawRating (Pixbuf pbuf, int val)
+		{
+			// Clean pixbuf
+			pbuf.Fill (0xffffff00);
+			
+			if (val == -1) //NotRated
+				return;
+			//Stars
+			for (int i = 0; i < MaxRating; i ++)
+				if (i <= val - MinRating)
+					IconRated.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+							pbuf, i * IconRated.Width, 0);
+				else
+					IconNotRated.CopyArea (0, 0, IconRated.Width, IconRated.Height, 
+							pbuf, i * IconRated.Height, 0);
+		}
+		
+		public override Pixbuf IconRated {
+			get {
+				if (icon_rated_small == null)
+					icon_rated_small = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-rated", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_rated_small;
+			}
+		}
+		
+		public override Pixbuf IconNotRated {
+			get {
+				if (icon_blank_small == null)
+					icon_blank_small = GtkUtil.TryLoadIcon (FSpot.Global.IconTheme, "rating-blank", 16, (Gtk.IconLookupFlags)0);
+				
+				return icon_blank_small;
+			}
+		}
+		
+		public override int NumLevels {
+			get { return max_rating - min_rating + 1; }
+		}
+	}
+}

Modified: trunk/src/XmpTagsImporter.cs
==============================================================================
--- trunk/src/XmpTagsImporter.cs	(original)
+++ trunk/src/XmpTagsImporter.cs	Tue Jan 15 16:07:30 2008
@@ -43,6 +43,8 @@
 		const string People = MetadataStore.IViewNS + "People";
 		const string Subject = MetadataStore.DcNS + "subject";
 		const string RdfType = MetadataStore.RdfNS + "type";
+		const string Rating = MetadataStore.XmpNS + "Rating";
+		const string Urgency = MetadataStore.PhotoshopNS + "Urgency";
 		
 		private class TagInfo {
 			// This class contains the Root tag name, and its Icon name (if any)
@@ -149,6 +151,8 @@
 		public void ProcessStore (MetadataStore store, Photo photo)
 		{
 			Hashtable descriptions = new Hashtable ();
+			uint rating = System.UInt32.MaxValue; 
+			uint urgency = System.UInt32.MaxValue;
 
 			foreach (Statement stmt in store) {
 				//StatementList list = null;
@@ -186,6 +190,28 @@
 					}
 					break;
 
+				case Urgency: // Used if Rating was not found
+				case Rating:
+					Literal l = stmt.Object as Literal;
+					if (l != null && l.Value != null && l.Value.Length > 0) {
+						uint tmp_ui;
+						try {
+							tmp_ui = System.Convert.ToUInt32 (l.Value);
+						} catch {
+							// Set rating to 0, and continue
+							System.Console.WriteLine ("Found illegal rating >{0}< in predicate {1}. Rating cleared",
+										 l.Value, stmt.Predicate.Uri);
+							tmp_ui = 0;
+						}
+						if (tmp_ui > 5) // Max rating allowed in F-Spot
+							tmp_ui = 5;
+						if (stmt.Predicate.Uri == Rating)
+							rating = tmp_ui;
+						else
+							urgency = tmp_ui == 0 ? 0 : tmp_ui - 1; // Urgency valid values 1 - 8
+					}	 
+					break;
+
 				case State:
 				case City:
 				case Country:
@@ -213,6 +239,12 @@
 			if (descriptions.Contains (UserComment))
 				photo.Description = descriptions [UserComment] as String;
 
+			// Use the old urgency, only if rating was not available.
+			if (urgency < System.UInt32.MaxValue)
+				photo.Rating = urgency;
+			if (rating < System.UInt32.MaxValue)
+				photo.Rating = rating;
+
 #if false	
 			//FIXME: looks like we are doing some questionable repurposing of tags here...
 

Modified: trunk/src/f-spot.glade
==============================================================================
--- trunk/src/f-spot.glade	(original)
+++ trunk/src/f-spot.glade	Tue Jan 15 16:07:30 2008
@@ -5604,6 +5604,15 @@
 			</child>
 		      </widget>
                     </child>
+ 		  <child>
+		    <widget class="GtkCheckMenuItem" id="display_ratings_menu_item">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Display _Ratings</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">True</property>
+		      <signal name="activate" handler="HandleDisplayRatings"/>
+		    </widget>
+		  </child>
                     <child>
                       <widget class="GtkSeparatorMenuItem" id="separator17">
                         <property name="visible">True</property>
@@ -5788,6 +5797,42 @@
                         <property name="visible">True</property>
                       </widget>
                     </child>
+
+ 		  <child>
+		    <widget class="GtkMenuItem" id="set_rating_filter1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Set Rating filter...</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="HandleSetRatingFilter"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkMenuItem" id="clear_rating_filter">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Clear Rating Filter</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="HandleClearRatingFilter"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="find_unrated">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Unrated Photos</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="HandleFindUnrated"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkSeparatorMenuItem" id="separator15">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+
+
                     <child>
                       <widget class="GtkMenuItem" id="set_date_range1">
                         <property name="visible">True</property>
@@ -10287,6 +10332,218 @@
       </widget>
     </child>
   </widget>
+
+
+<widget class="GtkDialog" id="set_rating_filter">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Set Rating Filter</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+  <property name="has_separator">False</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="vbox84">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="hbuttonbox13">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="button28">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-6</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="button29">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-ok</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox87">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkFrame" id="frame48">
+	      <property name="visible">True</property>
+	      <property name="label_xalign">0</property>
+	      <property name="label_yalign">0.5</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment64">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">12</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkSpinButton" id="minrating">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="climb_rate">1</property>
+		      <property name="digits">0</property>
+		      <property name="numeric">False</property>
+		      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+		      <property name="snap_to_ticks">False</property>
+		      <property name="wrap">False</property>
+		      <property name="adjustment">4 0 5 1 10 10</property>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label214">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Min Rating&lt;/b&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="type">label_item</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkFrame" id="frame49">
+	      <property name="visible">True</property>
+	      <property name="label_xalign">0</property>
+	      <property name="label_yalign">0.5</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment65">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">12</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkSpinButton" id="maxrating">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="climb_rate">1</property>
+		      <property name="digits">0</property>
+		      <property name="numeric">False</property>
+		      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+		      <property name="snap_to_ticks">False</property>
+		      <property name="wrap">False</property>
+		      <property name="adjustment">5 0 5 1 10 10</property>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label215">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Max Rating&lt;/b&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="type">label_item</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+
+
   <widget class="GtkDialog" id="repair_dialog">
     <property name="visible">True</property>
     <property name="title" translatable="yes">Repair</property>



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