f-spot r4009 - in trunk: . src src/Widgets



Author: rubenv
Date: Wed May 28 20:15:29 2008
New Revision: 4009
URL: http://svn.gnome.org/viewvc/f-spot?rev=4009&view=rev

Log:
2008-05-28  Ruben Vermeersch  <ruben savanne be>

	This commit adds the sidebar patch, as it is found in bugzilla. It
	provides the groundwork for my Summer of Code project and will be highly
	modified during the coming weeks. Stay Tuned!

	* src/InfoOverlay.cs: Switch to delayed updating.

	* src/MainWindow.cs: Show the new sidebar.

	* src/Makefile.am: Add the new files.

	* src/MetadataDisplay.cs: Added. Shows EXIF information (should probably
	be moved to the FSpot.Widgets namespace).

	* src/Preferences.cs: Add the (hidden) preference which stores the last
	displayed sidebar pane.

	* src/SingleView.cs: Show the sidebar in single view.

	* src/Widgets/InfoBox.cs: This brings the info box back into the stone
	age, with some modifications. The next step for my GSoC is to bring
	everything that happened to the InfoBox between July 2007 and now back.

	* src/Widgets/InfoVBox.cs: Added.

	* src/Widgets/MenuButton.cs: Added.

	* src/Widgets/Sidebar.cs: Added. A new switchable sidebar.

	* src/f-spot.glade: Nuke sidebar bits from the glade file, using pure GTK#
	now.


Added:
   trunk/src/MetadataDisplay.cs
   trunk/src/Widgets/InfoVBox.cs
   trunk/src/Widgets/MenuButton.cs
   trunk/src/Widgets/Sidebar.cs
Modified:
   trunk/ChangeLog
   trunk/src/InfoOverlay.cs
   trunk/src/MainWindow.cs
   trunk/src/Makefile.am
   trunk/src/Preferences.cs
   trunk/src/SingleView.cs
   trunk/src/Widgets/InfoBox.cs
   trunk/src/f-spot.glade

Modified: trunk/src/InfoOverlay.cs
==============================================================================
--- trunk/src/InfoOverlay.cs	(original)
+++ trunk/src/InfoOverlay.cs	Wed May 28 20:15:29 2008
@@ -11,24 +11,33 @@
 using FSpot.Widgets;
 
 namespace FSpot {
-	public class InfoItem : InfoBox {
+	public class InfoItem : InfoVBox {
 		BrowsablePointer item;
-
+		Delay update_delay;
+		
 		public InfoItem (BrowsablePointer item)
 		{
+			update_delay = new Delay (Update);
 			this.item = item;
 			item.Changed += HandleItemChanged;
 			HandleItemChanged (item, null);
 			VersionIdChanged += HandleVersionIdChanged;
-			ShowTags = true;
+			//ShowTags = true;
+		}
+		
+		public bool Update () {
+			UpdateSingleSelection (item.Current);
+			return false;
 		}
 
 		private void HandleItemChanged (BrowsablePointer sender, BrowsablePointerChangedArgs args)
 		{
-			Photo = item.Current;
+			update_delay.Start ();
+			//this.UpdateSingleSelection (item.Current);
+			//Photo = item.Current;
 		}
 
-		private void HandleVersionIdChanged (InfoBox box, uint version_id)
+		private void HandleVersionIdChanged (InfoVBox box, uint version_id)
 		{
 			Photo p = item.Current as Photo;
 			PhotoQuery q = item.Collection as PhotoQuery;

Modified: trunk/src/MainWindow.cs
==============================================================================
--- trunk/src/MainWindow.cs	(original)
+++ trunk/src/MainWindow.cs	Wed May 28 20:15:29 2008
@@ -28,6 +28,8 @@
 
 	Db db;
 
+	Sidebar sidebar;
+
 	TagSelectionWidget tag_selection_widget;
 	[Glade.Widget] Gtk.Window main_window;
 
@@ -41,7 +43,8 @@
 	[Glade.Widget] ScrolledWindow icon_view_scrolled;
 	[Glade.Widget] Box photo_box;
 	[Glade.Widget] Notebook view_notebook;
-	[Glade.Widget] ScrolledWindow tag_selection_scrolled;
+	
+	ScrolledWindow tag_selection_scrolled;
 
 	[Glade.Widget] Label status_label;
 
@@ -75,8 +78,6 @@
 	[Glade.Widget] MenuItem remove_tag;
 
 	// View
-	[Glade.Widget] MenuItem exif_data;
-	
 	[Glade.Widget] CheckMenuItem display_toolbar;
 	[Glade.Widget] CheckMenuItem display_sidebar;
 	[Glade.Widget] CheckMenuItem display_timeline;
@@ -125,6 +126,7 @@
 
 	[Glade.Widget] Gtk.HBox tagbar;
 	[Glade.Widget] Gtk.VBox tag_entry_container;
+	[Glade.Widget] Gtk.VBox tag_vbox;
 	TagEntry tag_entry;
 
 	Gtk.Toolbar toolbar;
@@ -137,7 +139,7 @@
 	Gtk.ToggleToolButton edit_button;
 
 	InfoBox info_box;
-	FSpot.InfoDisplay info_display;
+	MetadataDisplay info_display;
 	QueryView icon_view;
 	PhotoView photo_view;
 	FSpot.FullScreenView fsview;
@@ -295,9 +297,27 @@
 		ss_button.Clicked += HandleViewSlideShow;
 		ss_button.SetTooltip (ToolTips, Catalog.GetString ("View photos in a slideshow"), null);
 		toolbar.Insert (ss_button, -1);
+
+		sidebar = new Sidebar ();
+		tag_vbox.Add (sidebar);
+
+		tag_selection_scrolled = new ScrolledWindow ();
 		
 		tag_selection_widget = new TagSelectionWidget (db.Tags);
 		tag_selection_scrolled.Add (tag_selection_widget);
+
+		sidebar.AppendPage (tag_selection_scrolled, Catalog.GetString ("Tags"), "gtk-new");
+
+		info_display = new MetadataDisplay ();
+		info_display.ParentSidebar = sidebar;
+		sidebar.AppendPage (info_display, Catalog.GetString ("Exif"), "gtk-index");
+ 		
+		info_box = new InfoBox ();
+		info_box.ParentSidebar = sidebar;
+		info_box.VersionIdChanged += HandleInfoBoxVersionIdChange;
+		sidebar.AppendPage (info_box, Catalog.GetString ("Information"), "gtk-info");
+		sidebar.CloseRequested += HideSidebar;
+		sidebar.Show ();
 		
 		tag_selection_widget.Selection.Changed += HandleTagSelectionChanged;
 		tag_selection_widget.DragDataGet += HandleTagSelectionDragDataGet;
@@ -317,10 +337,6 @@
 		tag_selection_widget.RowActivated += HandleTagSelectionRowActivated;
 		
 		LoadPreference (Preferences.TAG_ICON_SIZE);
-
-		info_box = new InfoBox ();
-		info_box.VersionIdChanged += HandleInfoBoxVersionIdChange;
-		left_vbox.PackStart (info_box, false, true, 0);
 		
 		try {
 			query = new FSpot.PhotoQuery (db.Photos);
@@ -439,6 +455,10 @@
 		this.selection = new MainSelection (this);
 		this.selection.Changed += HandleSelectionChanged;
 		this.selection.ItemsChanged += HandleSelectionItemsChanged;
+		this.selection.Changed += info_box.HandleSelectionChanged;
+		this.selection.ItemsChanged += info_box.HandleSelectionItemsChanged;
+		this.selection.Changed += info_display.HandleSelectionChanged;
+		this.selection.ItemsChanged += info_display.HandleSelectionItemsChanged;
 
 		Mono.Addins.AddinManager.ExtensionChanged += PopulateExtendableMenus;
 		PopulateExtendableMenus (null, null);
@@ -452,6 +472,7 @@
 
 		UpdateFindByTagMenu ();
 
+		LoadPreference (Preferences.SIDEBAR_TOP_ENTRY);
 		LoadPreference (Preferences.SHOW_TOOLBAR);
 		LoadPreference (Preferences.SHOW_SIDEBAR);
 		LoadPreference (Preferences.SHOW_TIMELINE);
@@ -744,10 +765,6 @@
 
 	private void HandleSelectionChanged (IBrowsableCollection collection)
 	{
-		info_box.Photo = CurrentPhoto;
-		if (info_display != null)
-			info_display.Photo = CurrentPhoto;
-
 		UpdateMenus ();
 		UpdateTagEntryFromSelection ();
 		UpdateStatusLabel();	
@@ -757,7 +774,6 @@
 	{
 		UpdateMenus ();
 		UpdateTagEntryFromSelection ();
-		info_box.Update ();
 	}
 
 
@@ -1634,28 +1650,6 @@
 		Mono.Addins.Gui.AddinManagerWindow.Run (main_window);
 	}
 	
-	void HandleViewFullExif (object sender, EventArgs args)
-	{
-		if (info_display_window != null) {
-			info_display_window.Present ();
-			return;
-		}
-
-		info_display = new FSpot.InfoDisplay ();
-		info_display_window = new Gtk.Dialog (Catalog.GetString ("Metadata Browser"), 
-						      main_window, 
-						      Gtk.DialogFlags.NoSeparator | Gtk.DialogFlags.DestroyWithParent);
-		info_display_window.SetDefaultSize (400, 400);
-		Gtk.ScrolledWindow scroll = new ScrolledWindow ();
-		info_display_window.VBox.PackStart (scroll);
-		scroll.Add (info_display);
-
-		info_display.Photo = CurrentPhoto;
-	       
-		info_display_window.ShowAll ();
-		info_display_window.Destroyed += HandleInfoDisplayDestroy;
-	}
-
 	void HandleViewDirectory (object sender, EventArgs args)
 	{
 		Gtk.Window win = new Gtk.Window ("Directory View");
@@ -2671,6 +2665,10 @@
 				main_hpaned.Position = (int) val;
 			break;
 
+		case Preferences.SIDEBAR_TOP_ENTRY:
+			sidebar.SwitchTo ((string) val);
+			break;
+
 		case Preferences.TAG_ICON_SIZE:
 			int s = (int) val;
 			tag_icon_hidden.Active = (s == (int) Tag.IconSize.Hidden);
@@ -2869,7 +2867,6 @@
 		print.Sensitive = active_selection;
 		select_none.Sensitive = active_selection;
 		copy_location.Sensitive = active_selection;
-		exif_data.Sensitive = active_selection;
 		remove_from_catalog.Sensitive = active_selection;
 		
 		clear_rating_filter.Sensitive = (query.RatingRange != null);
@@ -3177,6 +3174,10 @@
 		query_widget.ShowBar ();
 		return;
 	}
+
+	public void HideSidebar (object o, EventArgs args) {
+		display_sidebar.Active = false;
+	}
 	
 	public void HandleKeyPressEvent (object sender, Gtk.KeyPressEventArgs args)
 	{

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Wed May 28 20:15:29 2008
@@ -158,6 +158,7 @@
 	$(srcdir)/MemorySurface.cs		\
 	$(srcdir)/MetaStore.cs			\
 	$(srcdir)/MetadataStore.cs		\
+	$(srcdir)/MetadataDisplay.cs		\
 	$(srcdir)/NullPreferenceBackend.cs	\
 	$(srcdir)/Operation.cs			\
 	$(srcdir)/PhotoImageView.cs		\
@@ -233,6 +234,8 @@
 	$(srcdir)/Widgets/ImageDisplay.cs	\
 	$(srcdir)/Widgets/ImageInfo.cs		\
 	$(srcdir)/Widgets/InfoBox.cs		\
+	$(srcdir)/Widgets/InfoVBox.cs		\
+	$(srcdir)/Widgets/MenuButton.cs		\
 	$(srcdir)/Widgets/PanZoom.cs		\
 	$(srcdir)/Widgets/PreviewPopup.cs 	\
 	$(srcdir)/Widgets/Push.cs		\
@@ -240,6 +243,7 @@
 	$(srcdir)/Widgets/RatingMenuItem.cs	\
 	$(srcdir)/Widgets/Reveal.cs		\
 	$(srcdir)/Widgets/ScalingIconView.cs	\
+	$(srcdir)/Widgets/Sidebar.cs	\
 	$(srcdir)/Widgets/SoftFocus.cs		\
 	$(srcdir)/Widgets/TagEntry.cs		\
 	$(srcdir)/Widgets/Tilt.cs		\

Added: trunk/src/MetadataDisplay.cs
==============================================================================
--- (empty file)
+++ trunk/src/MetadataDisplay.cs	Wed May 28 20:15:29 2008
@@ -0,0 +1,461 @@
+/*
+ * Widgets.Sidebar.cs
+ *
+ * Author(s)
+ * 	Mike Gemuende <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+using SemWeb;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using Gtk;
+using FSpot.Widgets;
+
+using Mono.Unix;
+
+namespace FSpot {
+	public class MetadataDisplay : ScrolledWindow {
+		Delay update_delay;
+		
+		/* 	This VBox only contains exif-data,
+			so it is seperated from other information */
+		VBox exif_vbox;
+		
+		VBox main_vbox;
+		Label exif_message;
+		State display;
+		
+		Sidebar sidebar;
+		
+		// stores list of the expanded expanders
+		List<string> open_list;
+		
+		ListStore extended_metadata;
+		
+		bool up_to_date = false;
+		
+		enum State {
+			exif,
+			message
+		};
+		
+		public MetadataDisplay ()
+		{
+			main_vbox = new VBox ();
+			main_vbox.Spacing = 6;
+			
+			exif_message = new Label (String.Empty);
+			exif_message.UseMarkup = true;
+			exif_message.LineWrap = true;
+			exif_vbox = new VBox ();
+			exif_vbox.Spacing = 6;
+			
+			main_vbox.PackStart (exif_vbox, false, false, 0);
+			AddWithViewport (exif_message);
+			((Viewport) Child).ShadowType = ShadowType.None;
+			BorderWidth = 3;
+			
+			display = State.message;
+			ExposeEvent += HandleExposeEvent;
+			
+			open_list = new List<string> ();
+			
+			// Create Expander and TreeView for
+			// extended metadata
+			TreeView tree_view = new TreeView ();
+			tree_view.HeadersVisible = false;
+			tree_view.RulesHint = true;
+						
+			TreeViewColumn col = new TreeViewColumn ();
+			col.Sizing = TreeViewColumnSizing.Autosize;
+			CellRenderer colr = new CellRendererText ();
+			col.PackStart (colr, false);
+
+			col.AddAttribute (colr, "markup", 0);
+
+			tree_view.AppendColumn (col);
+			
+			extended_metadata = new ListStore (typeof(string));
+			tree_view.Model = extended_metadata;
+			
+			Expander expander = new Expander (String.Format("<span weight=\"bold\"><small>{0}</small></span>", Catalog.GetString ("Extended Metadata")));
+			expander.UseMarkup = true;
+			expander.Add (tree_view);
+			
+			main_vbox.PackStart (expander, false, false, 6);
+			expander.ShowAll ();
+			
+			update_delay = new Delay (Update);
+			update_delay.Start ();
+		}
+		
+		public Sidebar ParentSidebar {
+			set {
+				this.sidebar = value;
+			}
+		}
+		
+		private Exif.ExifData exif_info;
+
+		private IBrowsableItem photo;
+		public IBrowsableItem Photo {
+			get {
+				return photo;
+			}
+			set {
+				photo = value;
+
+				if (exif_info != null)
+					exif_info.Dispose ();
+
+				if (photo != null) {
+					if (File.Exists (photo.DefaultVersionUri.LocalPath))
+						exif_info = new Exif.ExifData (photo.DefaultVersionUri.LocalPath);
+				} else {
+					exif_info = null;
+				}
+				
+				if (sidebar != null && !sidebar.isActive (this)) {
+					up_to_date = false;
+				} else {
+					update_delay.Start ();
+				}
+			}
+		}
+		
+		private void HandleExposeEvent (object sender, ExposeEventArgs args)
+		{
+			if (!up_to_date)
+			{
+				update_delay.Start ();
+			}
+		}
+		
+		public void HandleSelectionChanged (IBrowsableCollection collection) {
+			if (collection != null && collection.Count == 1)
+				Photo = collection [0];
+			else
+				Photo = null;
+		}
+		
+		public void HandleSelectionItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args) {
+			if (sidebar != null && !sidebar.isActive (this))
+				up_to_date = false;
+			else
+				update_delay.Start ();
+		}
+		
+		private ListStore AddExpander (string name, int pos)
+		{
+			TreeView tree_view = new TreeView ();
+			tree_view.HeadersVisible = false;
+			tree_view.RulesHint = true;
+						
+			TreeViewColumn col = new TreeViewColumn ();
+			col.Sizing = TreeViewColumnSizing.Autosize;
+			CellRenderer colr = new CellRendererText ();
+			col.PackStart (colr, false);
+
+			col.AddAttribute (colr, "markup", 0);
+
+			tree_view.AppendColumn (col);
+			
+			ListStore model = new ListStore (typeof(string));
+			tree_view.Model = model;
+			
+			Expander expander = new Expander (String.Format ("<span weight=\"bold\"><small>{0}</small></span>", name));
+			expander.UseMarkup = true;
+			expander.Add (tree_view);
+			
+			exif_vbox.PackStart (expander, false, false, 6);
+			exif_vbox.ReorderChild (expander, pos);
+			
+			if (open_list.Contains (name))
+				expander.Expanded = true;
+			
+			expander.Activated += HandleExpanderActivated;
+			
+			expander.ShowAll ();
+			
+			return model;
+		}
+		
+		public void HandleExpanderActivated (object sender, EventArgs e)
+		{
+			Expander expander = (Expander) sender;
+			if (expander.Expanded)
+				open_list.Add (expander.Label);
+			else
+				open_list.Remove (expander.Label);
+		}		
+		
+		private static string GetExportLabel (ExportItem export)
+		{
+			switch (export.ExportType) {
+			case ExportStore.FlickrExportType:
+				string[] split_token = export.ExportToken.Split (':');
+				return String.Format ("Flickr ({0})", split_token[1]);
+			case ExportStore.OldFolderExportType:	//Obsolete, remove after db rev4
+				return Catalog.GetString ("Folder");
+			case ExportStore.FolderExportType:
+				return Catalog.GetString ("Folder");
+			case ExportStore.PicasaExportType:
+				return Catalog.GetString ("Picasaweb");
+			case ExportStore.SmugMugExportType:
+				return Catalog.GetString ("SmugMug");
+			case ExportStore.Gallery2ExportType:
+				return Catalog.GetString ("Gallery2");
+			default:
+				return null;
+			}
+		}
+		
+		private static string GetExportUrl (ExportItem export)
+		{
+			switch (export.ExportType) {
+			case ExportStore.FlickrExportType:
+				string[] split_token = export.ExportToken.Split (':');
+				return String.Format ("http://www.{0}/photos/{1}/{2}/";, split_token[2],
+                                                      split_token[0], split_token[3]);
+			case ExportStore.FolderExportType:
+				Gnome.Vfs.Uri uri = new Gnome.Vfs.Uri (export.ExportToken);
+				return (uri.HasParent) ? uri.Parent.ToString () : export.ExportToken;
+			case ExportStore.Gallery2ExportType:
+				string[] split_item = export.ExportToken.Split (':');
+				return String.Format ("{0}:{1}?g2_itemId={2}",split_item[0], split_item[1], split_item[2]);
+			case ExportStore.OldFolderExportType:	//This is obsolete and meant to be removed once db reach rev4
+			case ExportStore.PicasaExportType:
+			case ExportStore.SmugMugExportType:
+				return export.ExportToken;
+			default:
+				return null;
+			}
+		}
+		
+		private bool Update ()
+		{
+			TreeIter iter;
+			ListStore model;
+			string name;
+			bool empty = true;
+			bool missing = false;
+			System.Exception error = null;
+			
+			up_to_date = true;
+			
+			int i = 0;
+			int index_of_expander = 0;
+			
+			// Write Exif-Data
+			if (exif_info != null) {
+				foreach (Exif.ExifContent content in exif_info.GetContents ()) {
+					Exif.ExifEntry [] entries = content.GetEntries ();
+					
+					i++;
+					
+					if (entries.Length < 1)
+						continue;
+					
+					empty = false;
+										
+					name = Exif.ExifUtil.GetIfdNameExtended ((Exif.Ifd)i - 1);
+					
+					if (index_of_expander >= exif_vbox.Children.Length)
+						model = AddExpander (name, index_of_expander);
+					else {
+						Expander expander = (Expander)exif_vbox.Children[index_of_expander];
+						if (expander.Label == name)
+							model = (ListStore)((TreeView)expander.Child).Model;
+						else {
+							model = AddExpander (name, index_of_expander);					
+						}
+					}
+					
+					model.GetIterFirst(out iter);
+				
+					foreach (Exif.ExifEntry entry in entries) {
+						string s;
+						
+						if (entry.Title != null)
+							s = String.Format ("{0}\n\t<small>{1}</small>", entry.Title, entry.Value);
+						else
+							s = String.Format ("Unknown Tag ID={0}\n\t<small>{1}</small>", entry.Tag.ToString (), entry.Value);
+												
+						if (model.IterIsValid(iter)) {
+							model.SetValue (iter, 0, s);
+							model.IterNext(ref iter);
+						} else
+							model.AppendValues (s);
+					}
+					
+					// remove rows, that are not used
+					while (model.IterIsValid(iter)) {
+						model.Remove (ref iter);
+					}
+					
+					index_of_expander++;
+				}
+			}
+			
+			
+			// Write Extended Metadata
+			if (photo != null) {
+				MetadataStore store = new MetadataStore ();
+				try {
+					using (ImageFile img = ImageFile.Create (photo.DefaultVersionUri)) {
+						if (img is SemWeb.StatementSource) {
+							StatementSource source = (StatementSource)img;
+							source.Select (store);
+						}
+					}
+				} catch (System.IO.FileNotFoundException) {
+					missing = true;
+				} catch (System.Exception e){
+					// Sometimes we don't get the right exception, check for the file
+					if (!System.IO.File.Exists (photo.DefaultVersionUri.LocalPath)) {
+						missing = true;
+					} else {
+						// if the file is there but we still got an exception display it.
+						error = e;
+					}
+				}
+				
+				model = extended_metadata;
+				model.GetIterFirst(out iter);
+				
+				if (store.StatementCount > 0) {
+					empty = false;
+
+					
+					foreach (Statement stmt in store) {
+						// Skip anonymous subjects because they are
+						// probably part of a collection
+						if (stmt.Subject.Uri == null && store.SelectSubjects (null, stmt.Subject).Length > 0)
+							continue;
+						
+						string title;
+						string value;
+						string s;
+
+						Description.GetDescription (store, stmt, out title, out value);
+						
+						if (value == null)
+						{
+							MemoryStore substore = store.Select (new Statement ((Entity)stmt.Object, null, null, null)).Load();
+							StringBuilder collection = new StringBuilder ();
+							collection.Append (title);
+							WriteCollection (substore, collection);
+							if (model.IterIsValid(iter))
+							{
+								model.SetValue (iter, 0, collection.ToString ());
+								model.IterNext(ref iter);
+							} else
+								model.AppendValues (collection.ToString ());
+						} else {
+							s = String.Format ("{0}\n\t<small>{1}</small>", title, value);
+							if (model.IterIsValid(iter))
+							{
+								model.SetValue (iter, 0, s);
+								model.IterNext(ref iter);
+							} else
+								model.AppendValues (s);
+						}
+					}
+					
+				} else {
+					// clear Extended Metadata
+					String s = String.Format ("<small>{0}</small>", Catalog.GetString ("No Extended Metadata Available"));
+					if (model.IterIsValid(iter))
+					{
+						model.SetValue (iter, 0, s);
+						model.IterNext(ref iter);
+					} else
+						model.AppendValues (s);
+				}
+				
+				// remove rows, that are not used
+				while (model.IterIsValid(iter)) {
+					model.Remove (ref iter);
+				}
+			} 
+			
+			if (empty) {
+				string msg;
+				if (photo == null) {
+				     msg = Catalog.GetString ("No active photo");
+				} else if (missing) {
+					msg = String.Format (Catalog.GetString ("The photo \"{0}\" does not exist"),
+					                                        photo.DefaultVersionUri);
+				} else {
+				     msg = Catalog.GetString ("No metadata available");
+
+					if (error != null) {
+						msg = String.Format ("<i>{0}</i>", error);
+					}
+				}
+				
+				exif_message.Markup = "<span weight=\"bold\">" + msg + "</span>";
+				
+				if (display == State.exif) {
+					// Child is a Viewport, (AddWithViewport in ctor)
+					((Viewport)Child).Remove (main_vbox);
+					((Viewport)Child).Add (exif_message);
+					display = State.message;
+					exif_message.Show ();
+				}
+			} else {
+				// remove Expanders, that are not used
+				while (index_of_expander < exif_vbox.Children.Length)
+					exif_vbox.Remove (exif_vbox.Children[index_of_expander]);
+				
+				if (display == State.message) {
+					// Child is a Viewport, (AddWithViewport in ctor)
+					((Viewport)Child).Remove (exif_message);
+					((Viewport)Child).Add (main_vbox);
+					display = State.exif;
+					main_vbox.ShowAll ();
+				}
+			}
+			
+			return false;		
+		}
+		
+		private void WriteCollection (MemoryStore substore, StringBuilder collection)
+		{
+			string type = null;
+
+			foreach (Statement stmt in substore) {
+				if (stmt.Predicate.Uri == MetadataStore.Namespaces.Resolve ("rdf:type")) {
+					string prefix;
+					MetadataStore.Namespaces.Normalize (stmt.Object.Uri, out prefix, out type);
+				}
+			}
+			
+			foreach (Statement sub in substore) {
+				if (sub.Object is Literal) {
+					string title;
+					string value = ((SemWeb.Literal)sub.Object).Value;
+					
+					Description.GetDescription (substore, sub, out title, out value);
+
+					if (type == null) 
+						collection.AppendFormat ("\n\t<small>{0}: {1}</small>", title, value);
+					else
+						collection.AppendFormat ("\n\t<small>{0}</small>", value);
+					
+				} else {
+					if (type == null) {
+						MemoryStore substore2 = substore.Select (new Statement ((Entity)sub.Object, null, null, null)).Load();
+						if (substore.StatementCount > 0)
+							WriteCollection (substore2, collection);
+					}
+				}
+			}
+		}
+		
+	}
+}

Modified: trunk/src/Preferences.cs
==============================================================================
--- trunk/src/Preferences.cs	(original)
+++ trunk/src/Preferences.cs	Wed May 28 20:15:29 2008
@@ -48,6 +48,7 @@
 		public const string GROUP_ADAPTOR_ORDER_ASC = "/apps/f-spot/ui/group_adaptor_sort_asc";
 		
 		public const string SIDEBAR_POSITION = "/apps/f-spot/ui/sidebar_size";
+		public const string SIDEBAR_TOP_ENTRY = "/apps/f-spot/ui/sidebar_top_entry";
 		public const string ZOOM = "/apps/f-spot/ui/zoom";
 
 		public const string EXPORT_EMAIL_SIZE = "/apps/f-spot/export/email/size";
@@ -146,6 +147,7 @@
 				return (int) Tag.IconSize.Large;
 		
 			case SIDEBAR_POSITION:
+			case SIDEBAR_TOP_ENTRY:
 			case ZOOM:
 				return null;
 

Modified: trunk/src/SingleView.cs
==============================================================================
--- trunk/src/SingleView.cs	(original)
+++ trunk/src/SingleView.cs	Wed May 28 20:15:29 2008
@@ -5,15 +5,17 @@
 
 using FSpot.Utils;
 using FSpot.UI.Dialog;
+using FSpot.Widgets;
 
 namespace FSpot {
 	public class SingleView {
 		[Glade.Widget] Gtk.HBox toolbar_hbox;
 		[Glade.Widget] Gtk.VBox info_vbox;
 		[Glade.Widget] Gtk.ScrolledWindow image_scrolled;
-		[Glade.Widget] Gtk.ScrolledWindow directory_scrolled;
 		[Glade.Widget] Gtk.HPaned info_hpaned;
 
+		Gtk.ScrolledWindow directory_scrolled;
+
 		[Glade.Widget] Gtk.CheckMenuItem side_pane_item;
 		[Glade.Widget] Gtk.CheckMenuItem toolbar_item;
 		[Glade.Widget] Gtk.CheckMenuItem filenames_item;
@@ -27,13 +29,16 @@
 
 		[Glade.Widget] Label status_label;
 
+		Sidebar sidebar;
+
 		protected Glade.XML xml;
 		private Gtk.Window window;
 		PhotoImageView image_view;
 		FSpot.Widgets.IconView directory_view;
 		private Uri uri;
 		
-		InfoDialog metadata_dialog;
+		InfoBox info_box;
+		MetadataDisplay info_display;
 		
 		UriCollection collection;
 		
@@ -108,8 +113,25 @@
 			directory_view.DisplayTags = false;
 			directory_view.DisplayDates = false;
 			directory_view.DisplayRatings = false;
+
+			directory_scrolled = new ScrolledWindow();
 			directory_scrolled.Add (directory_view);
 
+			sidebar = new Sidebar ();
+
+			info_vbox.Add (sidebar);
+			sidebar.AppendPage (directory_scrolled, Catalog.GetString ("Folder"), "gtk-directory");
+
+			info_display = new MetadataDisplay ();
+			info_display.ParentSidebar = sidebar;
+			sidebar.AppendPage (info_display, Catalog.GetString ("Exif"), "gtk-index");
+ 		
+			info_box = new InfoBox ();
+			info_box.ParentSidebar = sidebar;
+			sidebar.AppendPage (info_box, Catalog.GetString ("Information"), "gtk-info");
+			sidebar.CloseRequested += HandleHideSidePane;
+			sidebar.Show ();
+
 			ThumbnailGenerator.Default.OnPixbufLoaded += delegate { directory_view.QueueDraw (); };
 
 			image_view = new PhotoImageView (collection);
@@ -140,6 +162,23 @@
 			window.DeleteEvent += HandleDeleteEvent;
 			
 			collection.Changed += HandleCollectionChanged;
+
+			// wrap the methods to fit to the delegate
+			image_view.Item.Changed += delegate (BrowsablePointer pointer, BrowsablePointerChangedArgs old) {
+															IBrowsableItem [] item = {pointer.Current};
+															PhotoArray item_array = new PhotoArray (item);
+															info_display.HandleSelectionChanged (item_array);
+													};
+			
+			image_view.Item.Changed += delegate (BrowsablePointer pointer, BrowsablePointerChangedArgs old) {
+															IBrowsableItem [] item = {pointer.Current};
+															PhotoArray item_array = new PhotoArray (item);
+															info_box.HandleSelectionChanged (item_array);
+													};
+
+			image_view.Item.Collection.ItemsChanged += info_box.HandleSelectionItemsChanged;
+			image_view.Item.Collection.ItemsChanged += info_display.HandleSelectionItemsChanged;
+
 			UpdateStatusLabel ();
 			
 			if (collection.Count > 0)
@@ -225,8 +264,6 @@
 				image_view.Item.Index = ((FSpot.Widgets.IconView.SelectionCollection)selection).Ids[0];
 
 				zoom_scale.Value = image_view.NormalizedZoom;
-				if (metadata_dialog != null)
-					metadata_dialog.InfoDisplay.Photo = image_view.Item.Current;
 			}
 			UpdateStatusLabel ();
 		}
@@ -274,20 +311,6 @@
 			fsview.PlayPause ();
 		}
 	
-		private void HandleViewMetadata (object sender, System.EventArgs args)
-		{
-			if (metadata_dialog != null) {
-				metadata_dialog.Present ();
-				return;
-			}
-			
-			metadata_dialog = new InfoDialog (window);
-			metadata_dialog.InfoDisplay.Photo = image_view.Item.Current;
-			
-			metadata_dialog.ShowAll ();
-			metadata_dialog.Destroyed += HandleMetadataDestroyed;
-		}
-
 		private void HandleViewFilenames (object sender, System.EventArgs args)
 		{
 			directory_view.DisplayFilenames = filenames_item.Active; 
@@ -344,11 +367,6 @@
 			chooser.Destroy ();
 		}
 
-		private void HandleMetadataDestroyed (object sender, System.EventArgs args)
-		{
-			metadata_dialog = null;
-		}
-	
 		private bool SlideShow ()
 		{
 			IBrowsableItem [] items = new IBrowsableItem [collection.Count];

Modified: trunk/src/Widgets/InfoBox.cs
==============================================================================
--- trunk/src/Widgets/InfoBox.cs	(original)
+++ trunk/src/Widgets/InfoBox.cs	Wed May 28 20:15:29 2008
@@ -1,370 +1,319 @@
 /*
- * FSpot.Widgets.InfoBox
+ * InfoBox.cs
  *
  * Author(s)
  * 	Ettore Perazzoli
  * 	Larry Ewing  <lewing novell com>
  * 	Gabriel Burt
  *	Stephane Delcroix  <stephane delcroix org>
+ * 	Mike Gemuende <mike gemuende de>
+ * 	Ruben Vermeersch <ruben savanne be>
  *
  * This is free software. See COPYING for details.
  */
-
-
+ 
 using Gtk;
 using System;
 using System.IO;
-using FSpot;
-using SemWeb;
+using FSpot.Utils;
+using FSpot.Widgets;
 using Mono.Unix;
 
 
+namespace FSpot {
+	
 // FIXME TODO: We want to use something like EClippedLabel here throughout so it handles small sizes
 // gracefully using ellipsis.
 
-namespace FSpot.Widgets
-{
-	public class InfoBox : VBox {
+	public class InfoBox : ScrolledWindow {
 		Delay update_delay;
-	
-		private IBrowsableItem photo;
-		public IBrowsableItem Photo {
+		bool up_to_date = false;
+		Sidebar sidebar;
+		
+		
+		private IBrowsableCollection collection;
+		
+		public IBrowsableCollection Collection {
 			set {
-				photo = value;
-				update_delay.Start ();
+				collection = value;
+				
+				if (sidebar != null && !sidebar.isActive (this))
+					up_to_date = false;
+				else
+					update_delay.Start ();
+			}
+			
+			get {
+				return collection;
 			}
 		}
-	
-		private bool show_tags = false;
-		public bool ShowTags {
-			get { return show_tags; }
-			set {
-				if (show_tags == value)
-					return;
 
-				show_tags = value;
-				tag_view.Visible = show_tags;
+		public Sidebar ParentSidebar {
+			set {
+				this.sidebar = value;
 			}
 		}
-
+		
 		public delegate void VersionIdChangedHandler (InfoBox info_box, uint version_id);
 		public event VersionIdChangedHandler VersionIdChanged;
-	
-	
-		// Widgetry.	
-		private Label name_label;
-		private Label date_label;
-		private Label size_label;
-		private Label exposure_info_label;
-		private OptionMenu version_option_menu;
-		private TagView tag_view;
+		
+		// Widgetry.
+		private VBox main_vbox;
+		
+		private InfoVBox info_vbox;
+		private VBox locations_vbox;
+		private VBox tags_vbox;
+		
+		private int thumbnail_size = 32;
+		
+		private void HandleExposeEvent (object sender, ExposeEventArgs args)
+		{
+			if (!up_to_date)
+			{
+				update_delay.Start ();
+			}
+		}
 
-		private void HandleVersionIdChanged (PhotoVersionMenu menu)
+		private void HandleInfoVBoxVersionIdChanged (InfoVBox info_vbox, uint version_id)
 		{
 			if (VersionIdChanged != null)
-				VersionIdChanged (this, menu.VersionId);
+				VersionIdChanged (this, version_id);
 		}
-	
-		private Label CreateRightAlignedLabel (string text)
+
+		private void SetupWidgets ()
 		{
-			Label label = new Label ();
-			label.UseMarkup = true;
-			label.Markup = text;
-			label.Xalign = 1;
-	
-			return label;
+			main_vbox = new VBox ();
+			main_vbox.Spacing = 48;
+			
+			AddWithViewport (main_vbox);
+			((Viewport) Child).ShadowType = ShadowType.None;
+			BorderWidth = 3;
+			
+			info_vbox = new InfoVBox ();
+			info_vbox.VersionIdChanged += HandleInfoVBoxVersionIdChanged;
+			
+			VBox vbox = new VBox ();
+			vbox.Spacing = 12;
+			Label title = new Label (String.Format ("<b>{0}</b>", Catalog.GetString ("Image")));
+			title.UseMarkup = true;
+			title.Xalign = 0;
+			vbox.PackStart (title, false, false, 3);
+			vbox.PackStart (info_vbox, false, false, 3);
+			main_vbox.PackStart (vbox, false, false, 0);
+			
+			vbox = new VBox ();
+			vbox.Spacing = 12;
+
+			title = new Label (String.Format ("<b>{0}</b>", Catalog.GetString ("Exported Locations")));
+			title.UseMarkup = true;
+			title.Xalign = 0;
+			vbox.PackStart (title, false, false, 3);
+			
+			locations_vbox = new VBox ();
+			vbox.PackStart (locations_vbox, false, false, 3);
+			main_vbox.PackStart (vbox, false, false, 0);
+			
+			vbox = new VBox ();
+			vbox.Spacing = 12;
+			
+			title = new Label (String.Format ("<b>{0}</b>", Catalog.GetString ("Tags")));
+			title.UseMarkup = true;
+			title.Xalign = 0;
+			vbox.PackStart (title, false, false, 3);
+			
+			tags_vbox = new VBox ();
+			tags_vbox.Spacing = 3;
+			vbox.PackStart (tags_vbox, false, false, 3);
+			
+			main_vbox.PackStart (vbox, false, false, 0);
+			main_vbox.ShowAll ();
 		}
-	
-		const int TABLE_XPADDING = 3;
-		const int TABLE_YPADDING = 3;
-		static private Label AttachLabel (Table table, int row_num, Widget entry)
-		{
-			Label label = new Label (String.Empty);
-			label.Xalign = 0;
-			label.Selectable = true;
-			label.Ellipsize = Pango.EllipsizeMode.End;
-			label.Show ();
-	
-			table.Attach (label, 1, 2, (uint) row_num, (uint) row_num + 1,
-				      AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Expand | AttachOptions.Fill,
-				      (uint) entry.Style.XThickness + TABLE_XPADDING, (uint) entry.Style.YThickness);
-	
-			return label;
+		
+		public void HandleSelectionChanged (IBrowsableCollection collection) {
+			Collection = collection;
 		}
-	
-		private string default_exposure_string;
-		private Label exposure_name_label;
-		private void SetupWidgets ()
-		{
-			Table table = new Table (6, 2, false);
-			table.BorderWidth = 0;
-	
-			string name_pre = "<b>";
-			string name_post = "</b>";
-			table.Attach (CreateRightAlignedLabel (name_pre + Catalog.GetString ("name") + name_post), 0, 1, 0, 1,
-				      AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-			table.Attach (CreateRightAlignedLabel (name_pre + Catalog.GetString ("version") + name_post), 0, 1, 1, 2,
-				      AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-			table.Attach (CreateRightAlignedLabel (name_pre + Catalog.GetString ("date") + name_post + Environment.NewLine), 0, 1, 2, 3,
-				      AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-			table.Attach (CreateRightAlignedLabel (name_pre + Catalog.GetString ("size") + name_post), 0, 1, 3, 4,
-				      AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-			default_exposure_string = name_pre + Catalog.GetString ("exposure") + name_post;
-			exposure_name_label = CreateRightAlignedLabel (default_exposure_string);
-			table.Attach (exposure_name_label, 0, 1, 4, 5,
-				      AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-	
-			name_label = new Label ();
-			name_label.Ellipsize = Pango.EllipsizeMode.Middle;
-			name_label.Justify = Gtk.Justification.Left;
-			name_label.Selectable = true;
-			name_label.Xalign = 0;
-			table.Attach (name_label, 1, 2, 0, 1,
-				      AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Fill,
-				      3, 0);
-			
-			date_label = AttachLabel (table, 2, name_label);
-			size_label = AttachLabel (table, 3, name_label);
-			exposure_info_label = AttachLabel (table, 4, name_label);
-	
-			version_option_menu = new OptionMenu ();
-			table.Attach (version_option_menu, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-	
-			date_label.Text = Environment.NewLine;
-			exposure_info_label.Text = Environment.NewLine;
-	
-			tag_view = new TagView (MainWindow.ToolTips);
-			table.Attach (tag_view, 0, 2, 5, 6, AttachOptions.Fill, AttachOptions.Fill, TABLE_XPADDING, TABLE_YPADDING);
-			tag_view.Show ();
-			table.ShowAll ();
-	
-			Add (table);
+
+		public void HandleSelectionItemsChanged (IBrowsableCollection collection, BrowsableEventArgs args) {
+			if (sidebar != null && !sidebar.isActive (this))
+				up_to_date = false;
+			else
+				update_delay.Start ();
 		}
-	
+
 		private void Clear ()
 		{
-			name_label.Sensitive = false;
-	
-			version_option_menu.Sensitive = false;
-			version_option_menu.Menu = new Menu ();	// GTK doesn't like NULL here although that's what we want.
-	
-			name_label.Text = String.Empty;
-			date_label.Text = Environment.NewLine;
-			size_label.Text = String.Empty;
-			exposure_info_label.Text = Environment.NewLine;
-		}
-	
-		private class ImageInfo : StatementSink {
-			string width;
-			string height;
-			string aperture;
-			string fnumber;
-			string exposure;
-			string iso_speed;
-			bool add = true;
-			Resource iso_anon;
-	
-			MemoryStore store;
-			
-	#if USE_EXIF_DATE
-			DateTime date;
-	#endif
-			public ImageInfo (ImageFile img) 
-			{
-				// FIXME We use the memory store to hold the anonymous statements
-				// as they are added so that we can query for them later to 
-				// resolve anonymous nodes.
-				store = new MemoryStore ();
-	
-				if (img == null) 
-					return;
-	
-				if (img is StatementSource) {
-					SemWeb.StatementSource source = (SemWeb.StatementSource)img;
-					source.Select (this);
-	
-					// If we couldn't find the ISO speed because of the ordering
-					// search the memory store for the values
-					if (iso_speed == null && iso_anon != null) {
-						add = false;
-						store.Select (this);
-					}
-				}
-	
-				if (img is JpegFile) {
-					int real_width;
-					int real_height;
-	
-					JpegUtils.GetSize (img.Uri.LocalPath, out real_width, out real_height);
-					width = real_width.ToString ();
-					height = real_height.ToString ();
-				}
-	#if USE_EXIF_DATE
-				date = img.Date.ToLocalTime ();
-	#endif
-			}
-	
-			public bool Add (SemWeb.Statement stmt)
+			locations_vbox.Hide ();
+			tags_vbox.Hide ();
+		}		
+		
+		public bool Update ()
+		{
+			up_to_date = true;
+			if (collection != null && collection.Count > 0)
 			{
-				if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("tiff:ImageWidth")) {
-					if (width == null)
-						width = ((SemWeb.Literal)stmt.Object).Value;
-					} else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("tiff:ImageLength")) {
-					if (height == null)
-						height = ((SemWeb.Literal)stmt.Object).Value;
-				} else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:PixelXDimension"))
-					width = ((SemWeb.Literal)stmt.Object).Value;						      
-				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:PixelYDimension"))
-					height = ((SemWeb.Literal)stmt.Object).Value;
-				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ExposureTime"))
-					exposure = ((SemWeb.Literal)stmt.Object).Value;
-				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ApertureValue"))
-					aperture = ((SemWeb.Literal)stmt.Object).Value;
-				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:FNumber"))
-					fnumber = ((SemWeb.Literal)stmt.Object).Value;
-				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ISOSpeedRatings"))
-					iso_anon = stmt.Object;
-				else if (stmt.Subject == iso_anon && stmt.Predicate == MetadataStore.Namespaces.Resolve ("rdf:li"))
-					iso_speed = ((SemWeb.Literal)stmt.Object).Value;
-				else if (add && stmt.Subject.Uri == null)
-					store.Add (stmt);
-	
-				if (width == null || height == null || exposure == null || aperture == null || iso_speed == null)
-					return true;
+				if (collection.Count == 1)
+					UpdateSingleSelection (collection [0]);
 				else
-					return false;
-			}
-	
-			public string ExposureInfo {
-				get {
-					string info = String.Empty;
-	
-					if  (fnumber != null && fnumber != String.Empty) {
-						FSpot.Tiff.Rational rat = new FSpot.Tiff.Rational (fnumber);
-						info += String.Format ("f/{0:.0} ", rat.Value);
-					} else if (aperture != null && aperture != String.Empty) {
-						// Convert from APEX to fnumber
-						FSpot.Tiff.Rational rat = new FSpot.Tiff.Rational (aperture);
-						info += String.Format ("f/{0:.0} ", Math.Pow (2, rat.Value / 2));
-					}
-	
-					if (exposure != null && exposure != String.Empty)
-						info += exposure + " sec ";
-	
-					if (iso_speed != null && iso_speed != String.Empty)
-						info += Environment.NewLine + "ISO " + iso_speed;
-					
-					if (info == String.Empty)
-						return Catalog.GetString ("(None)");
-					
-					return info;
-				}
-			}
-	
-			public string Dimensions {
-				get {
-					if (width != null && height != null)
-						return String.Format ("{0}x{1}", width, height);
-					else 
-						return Catalog.GetString ("(Unknown)");
-				}
-			}
-	#if USE_EXIF_DATE
-			public string Date {
-				get {
-					if (date > DateTime.MinValue && date < DateTime.MaxValue)
-						return date.ToShortDateString () + Environment.NewLine + date.ToShortTimeString ();
-					else 
-						return Catalog.GetString ("(Unknown)");
-				}
+					UpdateMultipleSelection ();
+			} else {
+				info_vbox.Clear ();
+				Clear ();
 			}
-	#endif
+			return false;
+		}
+		
+		public void UpdateMultipleSelection ()
+		{
+			locations_vbox.Hide ();
+			tags_vbox.Hide ();
+			info_vbox.UpdateMultipleSelection (collection);
 		}
 			
-	
-		public bool Update ()
+		public void UpdateSingleSelection (IBrowsableItem photo)
 		{
-			ImageInfo info;
-	
+			info_vbox.UpdateSingleSelection (photo);
+			
 			if (photo == null) {
 				Clear ();
-				return false;
+				return;
 			}
 			
-			name_label.Text = photo.Name != null ? System.Uri.UnescapeDataString(photo.Name) : String.Empty;
-			try {
-				//using (new Timer ("building info")) {
-					using (ImageFile img = ImageFile.Create (photo.DefaultVersionUri))
-					{
-						info = new ImageInfo (img);
-					}
-					//}
-			} catch (System.Exception e) {
-				System.Console.WriteLine (e);
-				info = new ImageInfo (null);			
-			}
-	
-	
-			name_label.Sensitive = true;
-			exposure_info_label.Text = info.ExposureInfo;
-			if (exposure_info_label.Text.IndexOf (Environment.NewLine) != -1)
-				exposure_name_label.Markup = default_exposure_string + Environment.NewLine;
-			else
-				exposure_name_label.Markup = default_exposure_string;
-	
-			size_label.Text = info.Dimensions;
-	#if USE_EXIF_DATE
-			date_label.Text = info.Date;
-	#else
-			DateTime local_time = photo.Time.ToLocalTime ();
-			date_label.Text = String.Format ("{0}{2}{1}",
-				local_time.ToShortDateString (),
-				local_time.ToShortTimeString (),
-				Environment.NewLine
-			);
-	#endif
-			
-	
 			Photo p = photo as Photo;
-			if (p != null) {
-				version_option_menu.Visible = true;
-				version_option_menu.Sensitive = true;
-				PhotoVersionMenu menu = new PhotoVersionMenu (p);
-				menu.VersionIdChanged += new PhotoVersionMenu.VersionIdChangedHandler (HandleVersionIdChanged);
-				menu.WidthRequest = version_option_menu.Allocation.Width;
-				version_option_menu.Menu = menu;
+			if (p != null) {			
+				if (Core.Database != null)
+				{
+					foreach (Widget widget in locations_vbox.Children) {
+						locations_vbox.Remove (widget);
+					}
 				
-				uint i = 0;
-				foreach (uint version_id in p.VersionIds) {
-					if (version_id == p.DefaultVersionId) {
-						// FIXME GTK# why not just .History = i ?
-						version_option_menu.SetHistory (i);
-						break;
+					foreach (ExportItem export in Core.Database.Exports.GetByImageId (p.Id, p.DefaultVersionId)) {
+						string url = GetExportUrl (export);
+						string label = GetExportLabel (export);
+						if (url == null || label == null)
+							continue;
+
+						if (url.StartsWith ("/"))
+						{
+	            	    	// do a lame job of creating a URI out of local paths
+	            	    	url  = "file://" + url;
+	            	    }
+	            	    	LinkButton lb = new Gtk.LinkButton (label);
+		
+							lb.Relief = ReliefStyle.None;
+						
+							lb.Uri = url;
+							lb.Clicked += OnLinkButtonClicked;
+							locations_vbox.PackStart (lb, false, false, 0);
+	            	}
+	            }
+	            	    
+	            foreach (Widget widget in tags_vbox.Children) {
+	            	tags_vbox.Remove (widget);
+				}
+				
+				foreach (Tag tag in photo.Tags) {
+					HBox hbox = new HBox ();
+					hbox.Spacing = 6;
+					
+					Label label = new Label (tag.Name);
+					label.Xalign = 0;
+
+					Gdk.Pixbuf icon = tag.Icon;
+
+					Category category = tag.Category;
+					while (icon == null && category != null) {
+						icon = category.Icon;
+						category = category.Category;
+					}
+					
+					if (icon == null)
+						continue;
+					
+					Gdk.Pixbuf scaled_icon;
+					if (icon.Width == thumbnail_size) {
+						scaled_icon = icon;
+					} else {
+						scaled_icon = icon.ScaleSimple (thumbnail_size, thumbnail_size, Gdk.InterpType.Bilinear);
 					}
-					i++;
+					
+					hbox.PackStart (new Image (scaled_icon), false, false, 0);
+					hbox.PackStart (label, false, false, 0);
+					
+					tags_vbox.PackStart (hbox, false, false, 0);
 				}
-				if (show_tags)
-					tag_view.Current = p;
-			} else {
-				version_option_menu.Visible = false;
-				version_option_menu.Sensitive = false;
-				version_option_menu.Menu = null;
+	            	    
 			}
-	
-	
-			return false;
+			
+			if (locations_vbox.Children.Length != 0)
+				locations_vbox.ShowAll ();
+			else
+				locations_vbox.Hide ();
+
+			if (tags_vbox.Children.Length != 0)
+				tags_vbox.ShowAll ();
+			else
+				tags_vbox.Hide ();
 		}
-	
-	
+		
+		static void OnLinkButtonClicked (object o, EventArgs args)
+	    {
+	    	GnomeUtil.UrlShow ((o as LinkButton).Uri);
+	    }
+		
+		private static string GetExportLabel (ExportItem export)
+		{
+			switch (export.ExportType) {
+			case ExportStore.FlickrExportType:
+				string[] split_token = export.ExportToken.Split (':');
+				return String.Format ("Flickr ({0})", split_token[1]);
+			case ExportStore.OldFolderExportType:	//Obsolete, remove after db rev4
+				return Catalog.GetString ("Folder");
+			case ExportStore.FolderExportType:
+				return Catalog.GetString ("Folder");
+			case ExportStore.PicasaExportType:
+				return Catalog.GetString ("Picasaweb");
+			case ExportStore.SmugMugExportType:
+				return Catalog.GetString ("SmugMug");
+			case ExportStore.Gallery2ExportType:
+				return Catalog.GetString ("Gallery2");
+			default:
+				return null;
+			}
+		}
+			
+		private static string GetExportUrl (ExportItem export)
+		{
+			switch (export.ExportType) {
+			case ExportStore.FlickrExportType:
+				string[] split_token = export.ExportToken.Split (':');
+				return String.Format ("http://www.{0}/photos/{1}/{2}/";, split_token[2],
+	                                                     split_token[0], split_token[3]);
+			case ExportStore.FolderExportType:
+				Gnome.Vfs.Uri uri = new Gnome.Vfs.Uri (export.ExportToken);
+				return (uri.HasParent) ? uri.Parent.ToString () : export.ExportToken;
+			case ExportStore.Gallery2ExportType:
+				string[] split_item = export.ExportToken.Split (':');
+				return String.Format ("{0}:{1}?g2_itemId={2}",split_item[0], split_item[1], split_item[2]);
+			case ExportStore.OldFolderExportType:	//This is obsolete and meant to be removed once db reach rev4
+			case ExportStore.PicasaExportType:
+			case ExportStore.SmugMugExportType:
+				return export.ExportToken;
+			default:
+				return null;
+			}
+		}
+
 		// Constructor.
-	
-		public InfoBox () : base (false, 0)
+		public InfoBox ()
 		{
 			SetupWidgets ();
 			update_delay = new Delay (Update);
 			update_delay.Start ();
-	
-			BorderWidth = 2;
+			ExposeEvent += HandleExposeEvent;
+			
+			BorderWidth = 6;
 		}
+
 	}
 }
+			

Added: trunk/src/Widgets/InfoVBox.cs
==============================================================================
--- (empty file)
+++ trunk/src/Widgets/InfoVBox.cs	Wed May 28 20:15:29 2008
@@ -0,0 +1,404 @@
+/*
+ * InfoVBox.cs
+ *
+ * Author(s)
+ * 	Mike Gemuende <mike gemuende de>
+ *
+ * This is free software. See COPYING for details.
+ */
+ 
+using Gtk;
+using System;
+using System.IO;
+using SemWeb;
+using Mono.Unix;
+
+namespace FSpot.Widgets {
+	public class InfoVBox : VBox {
+		
+		public delegate void VersionIdChangedHandler (InfoVBox info_box, uint version_id);
+		public event VersionIdChangedHandler VersionIdChanged;		
+		
+		// Widgetry.	
+		private Label name_label;
+		private Label date_label;
+		private Label size_label;
+		private Label exposure_info_label;
+		private Label focal_length_label;
+		private Label camera_model_label;
+		private Label file_size_label;
+		
+		private TreeView version_chooser;
+		private OptionMenu version_option_menu;
+		
+		private void HandleVersionIdChanged (PhotoVersionMenu menu)
+		{
+			if (VersionIdChanged != null)
+				VersionIdChanged (this, menu.VersionId);
+		}
+		
+		private Widget CreateLeftAlignedLabel (string text)
+		{
+			Label label = new Label (text);
+			label.Xalign = 0;
+
+			return label;
+		}
+
+		static private Label AttachLabel (Table table, int row_num, Widget entry)
+		{
+			Label label = new Label (String.Empty);
+			label.Xalign = 0;
+			label.Selectable = true;
+			label.Ellipsize = Pango.EllipsizeMode.End;
+			label.Show ();
+
+			table.Attach (label, 1, 2, (uint) row_num, (uint) row_num + 1,
+				      AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Expand | AttachOptions.Fill,
+				      (uint) entry.Style.XThickness + 3, (uint) entry.Style.YThickness);
+
+			return label;
+		}
+
+		private void SetupWidgets ()
+		{
+			Spacing = 12;
+			
+			Table table = new Table (8, 2, false);
+
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Name:")), 0, 1, 0, 1,
+				      AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Version:")), 0, 1, 1, 2,
+				      AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Date:")), 0, 1, 2, 3,
+				      AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Size:")), 0, 1, 3, 4,
+				      AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Exposure:")), 0, 1, 4, 5,
+			          AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Focal Length:")), 0, 1, 5, 6,
+			          AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("Camera:")), 0, 1, 6, 7,
+			                AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			table.Attach (CreateLeftAlignedLabel (Catalog.GetString ("File Size:")), 0, 1, 7, 8,
+			          AttachOptions.Fill, AttachOptions.Fill, 3, 3);
+			
+			name_label = new Label ();
+			name_label.Ellipsize = Pango.EllipsizeMode.Middle;
+			name_label.Justify = Gtk.Justification.Left;
+			name_label.Selectable = true;
+			name_label.Xalign = 0;
+			table.Attach (name_label, 1, 2, 0, 1,
+				      AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Fill,
+				      3, 0);
+			
+			date_label = AttachLabel (table, 2, name_label);
+			size_label = AttachLabel (table, 3, name_label);
+			exposure_info_label = AttachLabel (table, 4, name_label);
+			focal_length_label = AttachLabel (table, 5, name_label);
+			camera_model_label = AttachLabel (table, 6, name_label);
+			file_size_label = AttachLabel (table, 7, name_label);
+			
+			version_option_menu = new OptionMenu ();
+			table.Attach (version_option_menu, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 3);
+
+			date_label.Text = Environment.NewLine;
+			exposure_info_label.Text = Environment.NewLine;
+			
+			PackStart (table, false, false, 3);
+
+			ShowAll ();
+		}
+		
+		private class ImageInfo : StatementSink {
+			string width;
+			string height;
+			string aperture;
+			string fnumber;
+			string exposure;
+			string iso_speed;
+			string focal_length;
+			string camera_model;
+			bool add = true;
+			Resource iso_anon;
+
+			MemoryStore store;
+			
+	#if USE_EXIF_DATE
+			DateTime date;
+	#endif
+			public ImageInfo (ImageFile img) 
+			{
+				// FIXME We use the memory store to hold the anonymous statements
+				// as they are added so that we can query for them later to 
+				// resolve anonymous nodes.
+				store = new MemoryStore ();
+
+				if (img == null) 
+					return;
+
+				if (img is StatementSource) {
+					SemWeb.StatementSource source = (SemWeb.StatementSource)img;
+					source.Select (this);
+
+					// If we couldn't find the ISO speed because of the ordering
+					// search the memory store for the values
+					if (iso_speed == null && iso_anon != null) {
+						add = false;
+						store.Select (this);
+					}
+				}
+
+				if (img is JpegFile) {
+					int real_width;
+					int real_height;
+
+					JpegUtils.GetSize (img.Uri.LocalPath, out real_width, out real_height);
+					width = real_width.ToString ();
+					height = real_height.ToString ();
+				}
+	#if USE_EXIF_DATE
+				date = img.Date.ToLocalTime ();
+	#endif
+			}
+
+			public bool Add (SemWeb.Statement stmt)
+			{
+				if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("tiff:ImageWidth")) {
+					if (width == null)
+						width = ((SemWeb.Literal)stmt.Object).Value;
+					} else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("tiff:ImageLength")) {
+					if (height == null)
+						height = ((SemWeb.Literal)stmt.Object).Value;
+				} else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:PixelXDimension"))
+					width = ((SemWeb.Literal)stmt.Object).Value;						      
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:PixelYDimension"))
+					height = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:FocalLength"))
+					focal_length = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("tiff:Model"))
+					camera_model = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ExposureTime"))
+					exposure = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ApertureValue"))
+					aperture = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:FNumber"))
+					fnumber = ((SemWeb.Literal)stmt.Object).Value;
+				else if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("exif:ISOSpeedRatings"))
+					iso_anon = stmt.Object;
+				else if (stmt.Subject == iso_anon && stmt.Predicate == MetadataStore.Namespaces.Resolve ("rdf:li"))
+					iso_speed = ((SemWeb.Literal)stmt.Object).Value;
+				else if (add && stmt.Subject.Uri == null)
+					store.Add (stmt);
+				
+				
+				
+				if (width == null || height == null || focal_length == null
+				        || camera_model == null || exposure == null || aperture == null
+				        || fnumber == null || iso_speed == null)
+					return true;
+				else
+					return false;
+			}
+			
+			public string FocalLength {
+				get {
+					if (focal_length == null)
+						return Catalog.GetString ("(Unknown)");
+					
+					string fl = focal_length;
+					
+					if (focal_length.Contains("/"))
+					{
+						string[] strings = focal_length.Split('/');
+						try {
+							if (strings.Length == 2)
+								fl = (double.Parse (strings[0]) / double.Parse (strings[1])).ToString ();
+						} catch (FormatException e) {
+						}
+					}
+					
+					return fl + " mm";
+				}
+			}
+			
+			public string CameraModel {
+				get {
+					if (focal_length != null)
+						return camera_model;
+					else
+						return Catalog.GetString ("(Unknown)");
+				}
+			}
+
+			public string ExposureInfo {
+				get {
+					string info = String.Empty;
+
+					if  (fnumber != null && fnumber != String.Empty) {
+						FSpot.Tiff.Rational rat = new FSpot.Tiff.Rational (fnumber);
+						info += String.Format ("f/{0:.0} ", rat.Value);
+					} else if (aperture != null && aperture != String.Empty) {
+						// Convert from APEX to fnumber
+						FSpot.Tiff.Rational rat = new FSpot.Tiff.Rational (aperture);
+						info += String.Format ("f/{0:.0} ", Math.Pow (2, rat.Value / 2));
+					}
+
+					if (exposure != null && exposure != String.Empty)
+						info += exposure + " sec ";
+
+					if (iso_speed != null && iso_speed != String.Empty)
+						info += Environment.NewLine + "ISO " + iso_speed;
+					
+					if (info == String.Empty)
+						return Catalog.GetString ("(None)");
+					
+					return info;
+				}
+			}
+
+			public string Dimensions {
+				get {
+					if (width != null && height != null)
+						return String.Format ("{0}x{1}", width, height);
+					else 
+						return Catalog.GetString ("(Unknown)");
+				}
+			}
+	#if USE_EXIF_DATE
+			public string Date {
+				get {
+					if (date > DateTime.MinValue && date < DateTime.MaxValue)
+						return date.ToShortDateString () + Environment.NewLine + date.ToShortTimeString ();
+					else 
+						return Catalog.GetString ("(Unknown)");
+				}
+			}
+	#endif
+		}
+		
+		public void Clear ()
+		{
+			name_label.Sensitive = false;
+
+			version_option_menu.Sensitive = false;
+			version_option_menu.Menu = new Menu ();	// GTK doesn't like NULL here although that's what we want.
+
+			name_label.Text = String.Empty;
+			date_label.Text = Environment.NewLine;
+			size_label.Text = String.Empty;
+			exposure_info_label.Text = Environment.NewLine;
+			focal_length_label.Text = String.Empty;
+			camera_model_label.Text = String.Empty;
+			file_size_label.Text = String.Empty;
+		}
+		
+		public void UpdateMultipleSelection (IBrowsableCollection collection)
+		{
+			name_label.Sensitive = false;
+
+			version_option_menu.Sensitive = false;
+			version_option_menu.Menu = new Menu ();	// GTK doesn't like NULL here although that's what we want.
+
+			name_label.Text = String.Empty;
+			date_label.Text = Environment.NewLine;
+			size_label.Text = String.Empty;
+			exposure_info_label.Text = Environment.NewLine;
+			focal_length_label.Text = String.Empty;
+			camera_model_label.Text = String.Empty;
+			file_size_label.Text = String.Empty;
+			
+			if (collection != null && collection.Count > 1) {
+				long size = 0;
+				try {
+					foreach (IBrowsableItem p in collection.Items) {
+						Gnome.Vfs.FileInfo file_info = new Gnome.Vfs.FileInfo (p.DefaultVersionUri.ToString ());
+						size += file_info.Size;
+					}
+					file_size_label.Text = Gnome.Vfs.Format.FileSizeForDisplay (size);
+				} catch (System.IO.FileNotFoundException) {
+					file_size_label.Text = Catalog.GetString("(One or more files not found)");
+				}
+			}
+		}
+		
+		public void UpdateSingleSelection (IBrowsableItem photo)
+		{
+			ImageInfo info;
+			
+			if (photo == null) {
+				Clear ();
+				return;
+			}
+			
+			name_label.Text = photo.Name != null ? photo.Name : String.Empty;
+			try {
+				//using (new Timer ("building info")) {
+					using (ImageFile img = ImageFile.Create (photo.DefaultVersionUri))
+					{
+						info = new ImageInfo (img);
+					}
+					//}
+			} catch (System.Exception e) {
+				System.Console.WriteLine (e);
+				info = new ImageInfo (null);			
+			}
+
+
+			name_label.Sensitive = true;
+			exposure_info_label.Text = info.ExposureInfo;
+			size_label.Text = info.Dimensions;
+			focal_length_label.Text = info.FocalLength;
+			camera_model_label.Text = info.CameraModel;
+			
+	#if USE_EXIF_DATE
+			date_label.Text = info.Date;
+	#else
+			date_label.Text = String.Format ("{0}{2}{1}",
+							 photo.Time.ToLocalTime ().ToShortDateString (),
+							 photo.Time.ToLocalTime ().ToShortTimeString (),
+							 Environment.NewLine);
+	#endif
+
+			Photo p = photo as Photo;
+			if (p != null) {
+				version_option_menu.Visible = true;
+				version_option_menu.Sensitive = true;
+				PhotoVersionMenu menu = new PhotoVersionMenu (p);
+				menu.VersionIdChanged += new PhotoVersionMenu.VersionIdChangedHandler (HandleVersionIdChanged);
+				// FIXME: here is a problem with FullScreenView and the VersionMenu
+				//menu.WidthRequest = version_option_menu.Allocation.Width;
+				version_option_menu.Menu = menu;
+				
+				uint i = 0;
+				foreach (uint version_id in p.VersionIds) {
+					if (version_id == p.DefaultVersionId) {
+						// FIXME GTK# why not just .History = i ?
+						version_option_menu.SetHistory (i);
+						break;
+					}
+					i++;
+				}
+				
+				try {
+					Gnome.Vfs.FileInfo file_info = new Gnome.Vfs.FileInfo (photo.DefaultVersionUri.ToString ());
+					file_size_label.Text = Gnome.Vfs.Format.FileSizeForDisplay (file_info.Size);
+				} catch (System.IO.FileNotFoundException) {
+					file_size_label.Text = Catalog.GetString("(File not found)");
+				}
+	            	    
+			} else {
+				version_option_menu.Visible = false;
+				version_option_menu.Sensitive = false;
+				version_option_menu.Menu = null;
+				file_size_label.Text = String.Empty;
+			}
+				
+		}
+		
+		public InfoVBox ()
+		{
+			SetupWidgets ();
+		}
+	}
+}

Added: trunk/src/Widgets/MenuButton.cs
==============================================================================
--- (empty file)
+++ trunk/src/Widgets/MenuButton.cs	Wed May 28 20:15:29 2008
@@ -0,0 +1,90 @@
+/*
+ * MenuButton.cs: a widget to mimic to e-combo-button from evolution
+ *
+ * Author(s)
+ * 	Stephane Delcroix  <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details
+ *
+ */
+
+using Gtk;
+
+namespace FSpot.Widgets
+{
+	public class MenuButton : Button
+	{
+		Label label;
+		Image image;
+		Arrow arrow;
+		Menu popup_menu;
+
+		public new string Label {
+			get { return label.Text; }
+			set { label.Text = value; }
+		}
+
+		public new Image Image {
+			get { return image; }
+		}
+
+		public ArrowType ArrowType {
+			get { return arrow.ArrowType; }
+			set { arrow.ArrowType = value; }
+		}
+
+		public Menu Menu {
+			get { return popup_menu; }
+			set { popup_menu = value; }
+		}
+
+		public MenuButton () : this (null)
+		{}
+
+		public MenuButton (string label) : this (label, null)
+		{}
+
+		public MenuButton (string label, Menu menu) : this (label, menu, ArrowType.Down)
+		{}
+
+		public MenuButton (string label, Menu menu, ArrowType arrow_type) : base ()
+		{
+			HBox hbox = new HBox ();
+			
+			this.image = new Image ();
+			hbox.PackStart (this.image, false, false, 1);
+			image.Show ();
+
+			this.label = new Label (label);
+			this.label.Xalign = 0;
+			hbox.PackStart (this.label, true, true, 1);
+			this.label.Show ();
+
+			this.arrow = new Arrow (arrow_type, ShadowType.None);
+			hbox.PackStart (arrow, false, false, 1);
+			arrow.Show ();
+
+			Menu = menu;
+
+			this.Add (hbox);
+			hbox.Show ();
+		}
+
+		protected override void OnClicked ()
+		{
+			if (popup_menu == null)
+				return;
+			
+			popup_menu.Popup (null, null, Position, 0, Gtk.Global.CurrentEventTime);
+		}
+
+		void Position (Menu menu, out int x, out int y, out bool push_in)
+		{
+			this.GdkWindow.GetOrigin (out x, out y);
+			x += Allocation.X;
+			y += Allocation.Y + Allocation.Height;
+			push_in = false;
+            menu.WidthRequest = Allocation.Width;
+		}
+	}
+}

Added: trunk/src/Widgets/Sidebar.cs
==============================================================================
--- (empty file)
+++ trunk/src/Widgets/Sidebar.cs	Wed May 28 20:15:29 2008
@@ -0,0 +1,130 @@
+/*
+ * Widgets.Sidebar.cs
+ *
+ * Author(s)
+ * 	Mike Gemuende <mike gemuende de>
+ *	Stephane Delcroix <stephane delcroix org>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using Gtk;
+using System;
+using System.Collections.Generic;
+
+namespace FSpot.Widgets {
+	public class Sidebar : VBox  {
+		
+		private HBox button_box;
+		private Notebook notebook;
+		private MenuButton choose_button;
+		private EventBox eventBox;
+		private Menu choose_menu;
+		private List<string> menu_list;
+		private List<string> image_list;
+
+		public event EventHandler CloseRequested;
+
+		public Sidebar () : base ()
+		{
+			button_box = new HBox ();
+			PackStart (button_box, false, false, 0);
+			
+			notebook = new Notebook ();
+			notebook.ShowTabs = false;
+            notebook.ShowBorder = false;
+			PackStart (notebook, true, true, 0);
+			
+			Button button = new Button ();
+			button.Image = new Image ("gtk-close", IconSize.Button);
+			button.Relief = ReliefStyle.None;
+			button.Pressed += HandleCloseButtonPressed;
+			button_box.PackEnd (button, false, true, 0);
+			
+			choose_button = new MenuButton ();
+			choose_button.Relief = ReliefStyle.None;
+					
+			eventBox = new EventBox ();
+			eventBox.Add (choose_button);
+			choose_button.ButtonPressEvent += HandleMouseScrollEvent;
+						
+			button_box.PackStart (eventBox, true, true, 0);
+			
+			choose_menu = new Menu ();
+			choose_button.Menu = choose_menu;
+
+			menu_list = new List<string> ();
+			image_list = new List<string> ();
+		}
+		
+		public void AppendPage (Widget widget, string label)
+		{
+			AppendPage (widget, label, null);
+		}
+
+		public void AppendPage (Widget widget, string label, string icon_name)
+		{	
+			notebook.AppendPage (widget, new Label (label));
+			
+			MenuItem item; 
+			if (icon_name == null)
+				item = new MenuItem (label);
+			else {
+				item = new ImageMenuItem (label);
+				(item as ImageMenuItem).Image = new Image (icon_name, IconSize.Menu);
+			}
+
+			item.Activated += HandleItemClicked;
+			choose_menu.Append (item);
+			item.Show ();
+			
+			if (notebook.Children.Length == 1)
+			{
+				choose_button.Label = label;
+				choose_button.Image.IconName = icon_name;
+			}
+			menu_list.Add (label);
+			image_list.Add (icon_name);
+		}
+		
+		public void HandleItemClicked (object o, EventArgs args)
+		{
+			Console.WriteLine (o);
+			SwitchTo (menu_list.IndexOf (((o as MenuItem).Child as Label).Text));
+		}
+		
+		public void HandleCloseButtonPressed (object sender, EventArgs args)
+		{
+			if (CloseRequested != null)
+				CloseRequested (this, args);
+		}
+		
+		public void HandleMouseScrollEvent (object o, EventArgs args)
+		{
+			System.Console.Out.WriteLine ("Hallo Welt");
+		}
+		
+		public void SwitchTo (int n)
+		{
+			if (n != notebook.CurrentPage)
+			{				
+				notebook.CurrentPage = n;
+				choose_button.Label = menu_list [n];
+				choose_button.Image.IconName = image_list [n];
+				Preferences.Set (Preferences.SIDEBAR_TOP_ENTRY, menu_list [n]);
+			}
+		}
+
+		public void SwitchTo (string name)
+		{
+			if (menu_list.Contains (name)) {
+				SwitchTo (menu_list.IndexOf (name));
+			}
+		}
+		
+		public bool isActive (Widget widget)
+		{
+			return (notebook.GetNthPage (notebook.CurrentPage) == widget);
+		}
+	}
+}

Modified: trunk/src/f-spot.glade
==============================================================================
--- trunk/src/f-spot.glade	(original)
+++ trunk/src/f-spot.glade	Wed May 28 20:15:29 2008
@@ -3419,15 +3419,6 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkMenuItem" id="exif_data">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Metadata Browser</property>
-                        <property name="use_underline">True</property>
-                        <signal name="activate" handler="HandleViewFullExif"/>
-                        <accelerator key="i" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-                      </widget>
-                    </child>
-                    <child>
                       <widget class="GtkSeparatorMenuItem" id="separator18">
                         <property name="visible">True</property>
                       </widget>
@@ -4003,21 +3994,6 @@
                         <child>
                           <placeholder/>
                         </child>
-                        <child>
-                          <widget class="GtkScrolledWindow" id="tag_selection_scrolled">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <property name="shadow_type">GTK_SHADOW_IN</property>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
                       </widget>
                     </child>
                     <child>
@@ -5184,15 +5160,6 @@
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkMenuItem" id="metadata_browser">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Metadata Browser</property>
-                        <property name="use_underline">True</property>
-                        <signal name="activate" handler="HandleViewMetadata"/>
-                        <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-                      </widget>
-                    </child>
-                    <child>
                       <widget class="GtkSeparatorMenuItem" id="separator26">
                         <property name="visible">True</property>
                       </widget>
@@ -5300,80 +5267,6 @@
             <child>
               <widget class="GtkVBox" id="info_vbox">
                 <property name="visible">True</property>
-                <child>
-                  <widget class="GtkHBox" id="hbox66">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkButton" id="button24">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="relief">GTK_RELIEF_NONE</property>
-                        <property name="response_id">0</property>
-                        <child>
-                          <widget class="GtkHBox" id="hbox70">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="sidebar_label">
-                                <property name="visible">True</property>
-                                <property name="xalign">0</property>
-                                <property name="label" translatable="yes">Folder</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkArrow" id="arrow1">
-                                <property name="visible">True</property>
-                                <property name="arrow_type">GTK_ARROW_DOWN</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkButton" id="button23">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="relief">GTK_RELIEF_NONE</property>
-                        <property name="response_id">0</property>
-                        <signal name="clicked" handler="HandleHideSidePane"/>
-                        <child>
-                          <widget class="GtkImage" id="image20">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-close</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkScrolledWindow" id="directory_scrolled">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                    <property name="shadow_type">GTK_SHADOW_IN</property>
-                    <child>
-                      <placeholder/>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
               </widget>
               <packing>
                 <property name="resize">False</property>



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