Re: Tag typing.



Herewith please find version 3 of the tag typing patch.  

A serviceable completion mechanism is now implemented.  Type the
beginning of a tag and press the TAB key to complete.  Pressing TAB
multiple times will cycle through the completion options.  The completed
portion of the tag name is selected so that hitting backspace or another
letter will cancel the completion.
        
The problems with it are discoverability and the fact that you don't
know what it will complete until you hit TAB.  The other problem is that
I complete in tag sort order, not in any sensible order like the
frequency of the tag usage or likelihood that the user wants to apply
this tag to this photo or anything like that.
        
Anyway, this implementation works really well for me and I doubt I'll do
much more development on this part of things.

The Delete key issue that Ruben reported is fixed now, and I made a few
cosmetic improvements here and there.

Unresolved issues: (1) we need to either go with the 't' mechanism David
proposed, or to reshortcut v, V and f, (2) we need to decide on sensible
hiding/showing behavior for the tagbar, and (3) the focus issue that
Gabriel and Ruben reported in the photo view is still there, I have no
idea how to fix that.

Future work may include: a more condensed viewing mode for the tag
sidebar so you can see more tags at once, a better completion system,
and a more flickr-like UI, with a list of tags arranged vertically
instead of this long horizontal bar.  Maybe.

Anyway, provided Larry likes this I think it's probably close to
CVS-ready.

Nat

Index: Db.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/Db.cs,v
retrieving revision 1.7
diff -u -r1.7 Db.cs
--- Db.cs	7 Oct 2005 21:35:39 -0000	1.7
+++ Db.cs	10 Nov 2005 05:13:36 -0000
@@ -24,7 +24,7 @@
 public abstract class DbStore {
 	// DbItem cache.
 
-	Hashtable item_cache;
+	protected Hashtable item_cache;
 	bool cache_is_immortal;
 
 	protected void AddToCache (DbItem item)
Index: MainWindow.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/MainWindow.cs,v
retrieving revision 1.228
diff -u -r1.228 MainWindow.cs
--- MainWindow.cs	2 Nov 2005 08:33:16 -0000	1.228
+++ MainWindow.cs	10 Nov 2005 05:13:37 -0000
@@ -83,6 +83,9 @@
 	[Glade.Widget] Gtk.Image near_image;
 	[Glade.Widget] Gtk.Image far_image;
 
+	[Glade.Widget] Gtk.HBox tagbar;
+	[Glade.Widget] Gtk.Entry tag_entry;
+
 	Gtk.Toolbar toolbar;
 
 	PhotoVersionMenu versions_submenu;
@@ -173,6 +176,7 @@
 		tag_selection_widget.DragDataGet += HandleTagSelectionDragDataGet;
 		tag_selection_widget.DragDrop += HandleTagSelectionDragDrop;
 		tag_selection_widget.DragBegin += HandleTagSelectionDragBegin;
+		tag_selection_widget.KeyPressEvent += HandleTagSelectionKeyPress;
 		Gtk.Drag.SourceSet (tag_selection_widget, Gdk.ModifierType.Button1Mask | Gdk.ModifierType.Button3Mask,
 				    tag_target_table, DragAction.Copy | DragAction.Move);
 
@@ -251,6 +255,7 @@
 		icon_view.DragMotion += HandleIconViewDragMotion;
 		icon_view.DragDrop += HandleIconViewDragDrop;
 		icon_view.DragDataReceived += HandleIconViewDragDataReceived;
+		icon_view.KeyPressEvent += HandleIconViewKeyPressEvent;
 
 		photo_view = new PhotoView (query, db.Photos);
 		photo_box.Add (photo_view);
@@ -260,6 +265,13 @@
 		photo_view.UpdateStarted += HandlePhotoViewUpdateStarted;
 		photo_view.UpdateFinished += HandlePhotoViewUpdateFinished;
 
+		// Tag typing: focus the tag entry if the user starts typing a tag
+		icon_view.KeyPressEvent += HandlePossibleTagTyping;
+		photo_view.KeyPressEvent += HandlePossibleTagTyping;
+		tag_entry.KeyPressEvent += HandleTagEntryKeyPressEvent;
+		tag_entry.FocusOutEvent += HandleTagEntryFocusOutEvent;
+		tag_entry.Changed += HandleTagEntryChanged;
+		
 		Gtk.Drag.DestSet (photo_view, DestDefaults.All, tag_target_table, 
 				  DragAction.Copy | DragAction.Move); 
 
@@ -273,6 +285,8 @@
 		UpdateMenus ();
 		main_window.ShowAll ();
 
+		tagbar.Hide ();
+
 		LoadPreference (Preferences.SHOW_TOOLBAR);
 		LoadPreference (Preferences.SHOW_SIDEBAR);
 		LoadPreference (Preferences.SHOW_TIMELINE);
@@ -455,7 +469,6 @@
 	private void RotateSelectedPictures (RotateCommand.Direction direction)
 	{
 		RotateCommand command = new RotateCommand (main_window);
-
 		
 		int [] selected_ids = SelectedIds ();
 		if (command.Execute (direction, SelectedPhotos (selected_ids))) {
@@ -505,13 +518,9 @@
 	{
 		if (args.Event.Button == 3)
 		{
-			TreePath path;
-			tag_selection_widget.Selection.UnselectAll ();
-			if (tag_selection_widget.GetPathAtPos ((int)args.Event.X, (int)args.Event.Y, out path)) {
-				tag_selection_widget.Selection.SelectPath (path);
-			}
 			TagPopup popup = new TagPopup ();
-			popup.Activate (args.Event, tag_selection_widget.TagAtPosition ((int)args.Event.X, (int)args.Event.Y));
+			popup.Activate (args.Event, tag_selection_widget.TagAtPosition ((int)args.Event.X, (int)args.Event.Y),
+					tag_selection_widget.TagHighlight ());
 			args.RetVal = true;
 		}
 	}
@@ -626,6 +635,8 @@
 
 			break;
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 
 #if SHOW_CALENDAR
@@ -789,6 +800,13 @@
 		args.RetVal = true;
 	}
 
+	void HandleIconViewKeyPressEvent (object sender, Gtk.KeyPressEventArgs args)
+	{
+		if (args.Event.Key == Gdk.Key.Delete) {
+			HandleRemoveCommand (sender, (EventArgs) args);
+		}
+	}
+
 	public void ImportUriList (UriList list) 
 	{
 		ImportCommand command = new ImportCommand (main_window);
@@ -824,6 +842,8 @@
 					AttachTags (tag_selection_widget.TagHighlight (), SelectedIds());
 				else 
 					AttachTags (tag_selection_widget.TagHighlight (), new int [] {item});
+
+				UpdateTagEntryFromSelection ();
 			}
 			break;
 		case (uint)TargetType.UriList:
@@ -851,7 +871,9 @@
 		info_box.Photo = CurrentPhoto;
 		if (info_display != null)
 			info_display.Photo = CurrentPhoto;
+
 		UpdateMenus ();
+		UpdateTagEntryFromSelection ();
 	}
 
 	void HandleDoubleClicked (IconView icon_view, int clicked_item)
@@ -960,6 +982,8 @@
 		foreach (int num in SelectedIds ()) {
 			AddTagExtended (num, new Tag [] {t});
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 	
 	void HandleFindTagMenuSelected (Tag t)
@@ -973,6 +997,8 @@
 			query.Photos [num].RemoveTag (t);
 			query.Commit (num);
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 
 	//
@@ -1338,16 +1364,71 @@
 			query.Photos [num].RemoveTag (tags);
 			query.Commit (num);
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 
-	public void HandleEditSelectedTag (object obj, EventArgs args)
+	public void HandleEditSelectedTag (object sender, EventArgs ea)
 	{
-		Tag [] tags = tag_selection_widget.TagHighlight ();
+		Tag [] tags = this.tag_selection_widget.TagHighlight ();
 		if (tags.Length != 1)
 			return;
+
+		HandleEditSelectedTagWithTag (tags [0]);
+	}
+
+	public void HandleEditSelectedTagWithTag (Tag tag)
+	{
+		if (tag == null)
+			return;
 		
 		TagCommands.Edit command = new TagCommands.Edit (db, main_window);
-		command.Execute (tags [0]);
+		command.Execute (tag);
+		if (view_mode == ModeType.IconView)
+			icon_view.QueueDraw ();
+	}
+
+	public void HandleMergeTagsCommand (object obj, EventArgs args)
+	{
+		Tag [] tags = this.tag_selection_widget.TagHighlight ();
+		if (tags.Length < 2)
+			return;
+
+		System.Array.Sort (tags, new TagRemoveComparer ());
+		string header = Mono.Posix.Catalog.GetString ("Merge the {0} selected tags?");
+		
+		header = String.Format (header, tags.Length);
+		string msg = Mono.Posix.Catalog.GetString("This operation will delete all but one of the selected tags.");
+		string ok_caption = Mono.Posix.Catalog.GetString ("_Merge tags");
+		
+		if (ResponseType.Ok != HigMessageDialog.RunHigConfirmation(main_window, 
+									   DialogFlags.DestroyWithParent, 
+									   MessageType.Warning, 
+									   header, 
+									   msg, 
+									   ok_caption))
+			return;
+		
+		// The surviving tag is tags [0].  removetags will contain
+		// the tags to be merged.
+		Tag [] removetags = new Tag [tags.Length - 1];
+		Array.Copy (tags, 1, removetags, 0, tags.Length - 1);
+
+
+		// Remove the defunct tags from all the photos and
+		// replace them with the new tag.
+		Photo [] photos = db.Photos.Query (tags, null);
+		foreach (Photo p in photos) {
+			p.RemoveTag (removetags);
+			p.AddTag (tags [0]);
+			db.Photos.Commit (p);
+		}
+
+		// Remove the defunct tags from the tag list.
+		db.Photos.Remove (removetags);
+
+		UpdateTagEntryFromSelection ();
+		icon_view.QueueDraw ();
 	}
 
 	void HandleAdjustColor (object sender, EventArgs args)
@@ -1685,15 +1766,24 @@
 	{
 		icon_view.Selection.Clear ();
 	}
-	
+
+	public void HandleTagSelectionKeyPress (object sender, Gtk.KeyPressEventArgs args)
+	{
+		if (args.Event.Key == Gdk.Key.Delete)
+			HandleDeleteSelectedTagCommand (sender, (EventArgs) args);
+	}
+
 	public void HandleDeleteSelectedTagCommand (object sender, EventArgs args)
 	{
 		Tag [] tags = this.tag_selection_widget.TagHighlight ();
 
 		System.Array.Sort (tags, new TagRemoveComparer ());
-		string header = Mono.Posix.Catalog.GetPluralString ("Delete the selected tag?",
-								    "Delete the {0} selected tags?", 
-								    tags.Length);
+
+		string header;
+		if (tags.Length == 1)
+			header = String.Format (Mono.Posix.Catalog.GetString ("Delete tag \"{0}\"?"), tags [0].Name);
+		else
+			header = String.Format (Mono.Posix.Catalog.GetString ("Delete the {0} selected tags?"), tags.Length);
 		
 		header = String.Format (header, tags.Length);
 		string msg = Mono.Posix.Catalog.GetString("If you delete a tag, all associations with photos are lost.");
@@ -1988,5 +2078,245 @@
 		remove_tag_from_selection.Sensitive = tag_sensitive && active_selection;
 	}
 
+	// Tag typing
+
+	private ArrayList selected_photos_tagnames;
+
+	private void UpdateTagEntryFromSelection ()
+	{
+		Hashtable taghash = new Hashtable ();
+
+		Photo [] sel = SelectedPhotos (SelectedIds ());
+		foreach (Photo p in sel) {
+			foreach (Tag t in p.Tags) {
+				int count = 1;
+				string tagname = t.Name;
+
+				if (taghash.Contains (tagname))
+					count = ((int) taghash [tagname]) + 1;
+
+				taghash [tagname] = count;
+			}
+		}
+
+		selected_photos_tagnames = new ArrayList ();
+		foreach (string tagname in taghash.Keys)
+			if ((int) (taghash [tagname]) == sel.Length)
+				selected_photos_tagnames.Add (tagname);
+		selected_photos_tagnames.Sort ();
+
+		string taglist = "";
+		foreach (string tagname in selected_photos_tagnames) {
+			if (taglist == "")
+				taglist = tagname;
+			else
+				taglist = taglist + ", " + tagname;
+		}
+
+		tag_entry.Text = taglist;
+
+		ClearTagCompletions ();
+	}
+
+	public void HandlePossibleTagTyping (object sender, Gtk.KeyPressEventArgs args)
+	{
+		if (tagbar.Visible && tag_entry.HasFocus)
+			return;
+
+		char c = System.Convert.ToChar (Gdk.Keyval.ToUnicode ((uint) args.Event.Key));
+		if (! System.Char.IsLetter (c))
+			return;
+		
+		if (tag_entry.Text.Length > 0)
+			tag_entry.Text += ", ";
+		tag_entry.Text += c;
+
+		tagbar.Show ();
+		tag_entry.GrabFocus ();
+		tag_entry.SelectRegion (-1, -1);
+	}
+
+	// "Activate" means the user pressed the enter key
+	public void HandleTagEntryActivate (object sender, EventArgs args)
+	{
+		string [] tagnames = GetTypedTagNames ();
+		int [] selected_photos = SelectedIds ();
+
+		if (selected_photos == null || tagnames == null)
+			return;
+		
+		// Add any new tags to the selected photos
+		Category default_category = null;
+		Tag [] selection = tag_selection_widget.TagHighlight ();
+		if (selection.Length > 0) {
+			if (selection [0] is Category)
+				default_category = (Category) selection [0];
+			else
+				default_category = selection [0].Category;
+		}
+
+		foreach (string tagname in tagnames) {
+			if (tagname.Length == 0)
+				continue;
+			
+			Tag t = db.Tags.GetTagByName (tagname);
+			if (t == null) {
+				t = db.Tags.CreateTag (default_category, tagname);
+				db.Tags.Commit (t);
+			}
+
+			Tag [] tags = new Tag [1];
+			tags [0] = t;
+
+			foreach (int num in selected_photos)
+				AddTagExtended (num, tags);
+		}
+
+		// Remove any removed tags from the selected photos
+		foreach (string tagname in selected_photos_tagnames) {
+			if (! IsTagInList (tagnames, tagname)) {
+				
+				Tag tag = db.Tags.GetTagByName (tagname);
+
+				foreach (int num in selected_photos) {
+					query.Photos [num].RemoveTag (tag);
+					query.Commit (num);
+				}
+			}
+		}
+
+		UpdateTagEntryFromSelection ();
+		if (view_mode == ModeType.IconView) {
+			icon_view.QueueDraw ();
+			icon_view.GrabFocus ();
+		} else {
+			photo_view.QueueDraw ();
+			photo_view.GrabFocus ();
+		}
+	}
+
+	private void HideTagbar ()
+	{
+		if (! tagbar.Visible)
+			return;
+		
+		// Cancel any pending edits...
+		UpdateTagEntryFromSelection ();
+
+		tagbar.Hide ();
+
+		if (view_mode == ModeType.IconView)
+			icon_view.GrabFocus ();
+		else
+			photo_view.GrabFocus ();
+
+		ClearTagCompletions ();
+	}
+
+	public void HandleTagBarCloseButtonPressed (object sender, EventArgs args)
+	{
+		HideTagbar ();
+	}
+
+	public void HandleTagEntryKeyPressEvent (object sender, Gtk.KeyPressEventArgs args)
+	{
+		args.RetVal = false;
+
+		if (args.Event.Key == Gdk.Key.Escape) {
+			HideTagbar (); 
+			args.RetVal = true;
+		} else if (args.Event.Key == Gdk.Key.Tab) {
+			DoTagCompletion ();
+			args.RetVal = true;
+		} else
+			ClearTagCompletions ();
+	}
+
+	bool tag_ignore_changes = false;
+	public void HandleTagEntryChanged (object sender, EventArgs args)
+	{
+		if (tag_ignore_changes)
+			return;
+
+		ClearTagCompletions ();
+	}
+
+	int tag_completion_index = -1;
+	Tag [] tag_completions;
+	string tag_completion_typed_so_far;
+	int tag_completion_typed_position;
+	private void DoTagCompletion ()
+	{
+		string completion;
+		
+		if (tag_completion_index != -1) {
+			tag_completion_index = (tag_completion_index + 1) % tag_completions.Length;
+		} else {
+
+			tag_completion_typed_position = tag_entry.Position;
+		    
+			string right_of_cursor = tag_entry.Text.Substring (tag_completion_typed_position);
+			if (right_of_cursor.Length > 1)
+				return;
+
+			int last_comma = tag_entry.Text.LastIndexOf (',');
+			if (last_comma > tag_completion_typed_position)
+				return;
+
+			tag_completion_typed_so_far = tag_entry.Text.Substring (last_comma + 1).TrimStart (new char [] {' '});
+			if (tag_completion_typed_so_far == null || tag_completion_typed_so_far.Length == 0)
+				return;
+
+			tag_completions = db.Tags.GetTagsByNameStart (tag_completion_typed_so_far);
+			if (tag_completions == null)
+				return;
+
+			tag_completion_index = 0;
+		}
+
+		tag_ignore_changes = true;
+		completion = tag_completions [tag_completion_index].Name.Substring (tag_completion_typed_so_far.Length);
+		tag_entry.Text = tag_entry.Text.Substring (0, tag_completion_typed_position) + completion;
+		tag_ignore_changes = false;
+
+		tag_entry.Position = tag_entry.Text.Length;
+		tag_entry.SelectRegion (tag_completion_typed_position, tag_entry.Text.Length);
+	}
+
+	private void ClearTagCompletions ()
+	{
+		tag_completion_index = -1;
+		tag_completions = null;
+	}
+
+
+	public void HandleTagEntryFocusOutEvent (object sender, EventArgs args)
+	{
+		UpdateTagEntryFromSelection ();
+		//		HideTagbar ();
+	}
+
+	private string [] GetTypedTagNames ()
+	{
+		string [] tagnames = tag_entry.Text.Split (new char [] {','});
+
+		ArrayList list = new ArrayList ();
+		for (int i = 0; i < tagnames.Length; i ++) {
+			string s = tagnames [i].Trim ();
+
+			if (s.Length > 0)
+				list.Add (s);
+		}
+
+		return (string []) (list.ToArray (typeof (string)));
+	}
+
+	private bool IsTagInList (string [] tags, string tag)
+	{
+		foreach (string t in tags)
+			if (t == tag)
+				return true;
+		return false;
+	}
 }
 
Index: PhotoStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/PhotoStore.cs,v
retrieving revision 1.72
diff -u -r1.72 PhotoStore.cs
--- PhotoStore.cs	2 Nov 2005 08:33:15 -0000	1.72
+++ PhotoStore.cs	10 Nov 2005 05:13:37 -0000
@@ -1273,7 +1273,7 @@
 		db.Photos.Commit (me_in_sf);
 
 		me_in_sf.RemoveTag (favorites_tag);
-		me_in_sf.Description = "Myself and the SF skyline";
+     		me_in_sf.Description = "Myself and the SF skyline";
 		me_in_sf.CreateVersion ("cropped", Photo.OriginalVersionId);
 		me_in_sf.CreateVersion ("UM-ed", Photo.OriginalVersionId);
 		db.Photos.Commit (me_in_sf);
Index: PhotoView.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/PhotoView.cs,v
retrieving revision 1.68
diff -u -r1.68 PhotoView.cs
--- PhotoView.cs	2 Nov 2005 08:33:15 -0000	1.68
+++ PhotoView.cs	10 Nov 2005 05:13:37 -0000
@@ -253,7 +253,6 @@
 		}
 
 		Photo photo = (Photo)Item.Current;
-		Exif.ExifData exif_data = new Exif.ExifData (photo.DefaultVersionPath);
 
 		Pixbuf edited;
 		if (redeye) {
@@ -273,7 +272,6 @@
 		// be fixed there.
 		photo_view.Pixbuf = edited;
 		photo_view.UnsetSelection ();
-		bool version = false;
 
 		try {
 			bool create_version = photo.DefaultVersionId == Photo.OriginalVersionId;
Index: TagCommands.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagCommands.cs,v
retrieving revision 1.28
diff -u -r1.28 TagCommands.cs
--- TagCommands.cs	28 Oct 2005 19:52:30 -0000	1.28
+++ TagCommands.cs	10 Nov 2005 05:13:37 -0000
@@ -323,6 +323,7 @@
 			}
 			
 			this.Dialog.Destroy ();
+
 			return success;
 		}
 
Index: TagPopup.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagPopup.cs,v
retrieving revision 1.3
diff -u -r1.3 TagPopup.cs
--- TagPopup.cs	22 Oct 2005 06:10:27 -0000	1.3
+++ TagPopup.cs	10 Nov 2005 05:13:37 -0000
@@ -12,7 +12,7 @@
 using System;
 
 public class TagPopup {
-	public void Activate (Gdk.EventButton eb, Tag tag)
+	public void Activate (Gdk.EventButton eb, Tag tag, Tag [] tags)
 	{
 		int count = MainWindow.Toplevel.SelectedIds ().Length;
 		Gtk.Menu popup_menu = new Gtk.Menu ();
@@ -24,19 +24,30 @@
 		
 		GtkUtil.MakeMenuSeparator (popup_menu);
 
-		GtkUtil.MakeMenuItem (popup_menu, Mono.Posix.Catalog.GetString ("Edit Tag"),
-				      new EventHandler (MainWindow.Toplevel.HandleEditSelectedTag), tag != null);
+		string editstr = String.Format (Mono.Posix.Catalog.GetString ("Edit Tag \"{0}\""), tag.Name);
+		GtkUtil.MakeMenuItem (popup_menu, editstr, delegate { MainWindow.Toplevel.HandleEditSelectedTagWithTag (tag); }, true);
 
-		GtkUtil.MakeMenuItem (popup_menu, Mono.Posix.Catalog.GetString ("Delete Tag"),
-				      new EventHandler (MainWindow.Toplevel.HandleDeleteSelectedTagCommand), tag != null);
-				      
+		GtkUtil.MakeMenuItem (popup_menu,
+				      Mono.Posix.Catalog.GetPluralString ("Delete Tag", "Delete Tags", tags.Length),
+				      new EventHandler (MainWindow.Toplevel.HandleDeleteSelectedTagCommand), tags != null && tags.Length > 0);
+		
 		GtkUtil.MakeMenuSeparator (popup_menu);
 
-		GtkUtil.MakeMenuItem (popup_menu, Mono.Posix.Catalog.GetString ("Attach Tag To Selection"),
+		GtkUtil.MakeMenuItem (popup_menu,
+				      Mono.Posix.Catalog.GetPluralString ("Attach Tag To Selection", "Attach Tags To Selection", tags.Length),
 				      new EventHandler (MainWindow.Toplevel.HandleAttachTagCommand), count > 0);
 
-		GtkUtil.MakeMenuItem (popup_menu, Mono.Posix.Catalog.GetString ("Remove Tag From Selection"),
+		GtkUtil.MakeMenuItem (popup_menu,
+				      Mono.Posix.Catalog.GetPluralString ("Remove Tag From Selection", "Remove Tags From Selection", tags.Length),
 				      new EventHandler (MainWindow.Toplevel.HandleRemoveTagCommand), count > 0);
+
+		if (tags.Length > 1) {
+			GtkUtil.MakeMenuSeparator (popup_menu);
+
+			GtkUtil.MakeMenuItem (popup_menu, Mono.Posix.Catalog.GetString ("Merge Tags"),
+					      new EventHandler (MainWindow.Toplevel.HandleMergeTagsCommand), tags.Length > 1);
+
+		}
 
 		popup_menu.Popup (null, null, null, eb.Button, eb.Time);
 	}
Index: TagSelectionWidget.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagSelectionWidget.cs,v
retrieving revision 1.21
diff -u -r1.21 TagSelectionWidget.cs
--- TagSelectionWidget.cs	2 Nov 2005 05:35:57 -0000	1.21
+++ TagSelectionWidget.cs	10 Nov 2005 05:13:37 -0000
@@ -290,7 +290,7 @@
 	{
 		TreeIter root = TreeIter.Zero;
 		iter = TreeIter.Zero;
-	
+
 		bool valid = Model.GetIterFirst (out root);
 		
 		while (valid) {
@@ -399,12 +399,14 @@
 	
 	private void HandleTagCreated (Tag tag) 
 	{
-		TreeIter iter;
+		TreeIter iter = TreeIter.Zero;
 
-		if (TreeIterForTag (tag.Category, out iter)) {
-			// create dialog doesn't let you create a top level tag...
-			InsertInOrder (iter, false, tag);
-		}
+		if (tag.Category != tag_store.RootCategory)
+			TreeIterForTag (tag.Category, out iter);
+
+		InsertInOrder (iter,
+			       tag.Category.Name == tag_store.RootCategory.Name,
+			       tag);
 	}
 	
 	private void HandleTagChanged (Tag tag) 
Index: TagStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagStore.cs,v
retrieving revision 1.20
diff -u -r1.20 TagStore.cs
--- TagStore.cs	28 Oct 2005 19:52:30 -0000	1.20
+++ TagStore.cs	10 Nov 2005 05:13:37 -0000
@@ -268,6 +268,30 @@
 		}
 	}
 
+	public Tag GetTagByName (string name)
+	{
+		foreach (Tag t in this.item_cache.Values) {
+			if (t.Name.ToLower () == name.ToLower ())
+				return t;
+		}
+
+		return null;
+	}
+
+	public Tag [] GetTagsByNameStart (string s)
+	{
+		ArrayList l = new ArrayList ();
+		foreach (Tag t in this.item_cache.Values) {
+			if (t.Name.ToLower ().StartsWith (s.ToLower ()))
+				l.Add (t);
+		}
+
+		if (l.Count == 0)
+			return null;
+
+		return (Tag []) (l.ToArray (typeof (Tag)));
+	}
+
 	// In this store we keep all the items (i.e. the tags) in memory at all times.  This is
 	// mostly to simplify handling of the parent relationship between tags, but it also makes it
 	// a little bit faster.  We achieve this by passing "true" as the cache_is_immortal to our
Index: f-spot.glade
===================================================================
RCS file: /cvs/gnome/f-spot/src/f-spot.glade,v
retrieving revision 1.115
diff -u -r1.115 f-spot.glade
--- f-spot.glade	7 Nov 2005 20:06:48 -0000	1.115
+++ f-spot.glade	10 Nov 2005 05:13:38 -0000
@@ -6735,7 +6735,7 @@
 		      <signal name="activate" handler="HandleSendMailCommand" last_modification_time="Mon, 07 Jun 2004 22:31:01 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image22">
+			<widget class="GtkImage" id="image24">
 			  <property name="visible">True</property>
 			  <property name="stock">gnome-stock-mail-fwd</property>
 			  <property name="icon_size">1</property>
@@ -6885,7 +6885,6 @@
 		      <property name="label" translatable="yes">_Remove From Catalog</property>
 		      <property name="use_underline">True</property>
 		      <signal name="activate" handler="HandleRemoveCommand" last_modification_time="Thu, 10 Jun 2004 03:21:10 GMT"/>
-		      <accelerator key="Delete" modifiers="0" signal="activate"/>
 		    </widget>
 		  </child>
 
@@ -7405,7 +7404,90 @@
 		  <property name="spacing">0</property>
 
 		  <child>
-		    <placeholder/>
+		    <widget class="GtkHBox" id="tagbar">
+		      <property name="border_width">2</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">1</property>
+
+		      <child>
+			<widget class="GtkLabel" id="label160">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Tags: </property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">False</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="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkEntry" id="tag_entry">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="has_focus">True</property>
+			  <property name="editable">True</property>
+			  <property name="visibility">True</property>
+			  <property name="max_length">0</property>
+			  <property name="text" translatable="yes"></property>
+			  <property name="has_frame">True</property>
+			  <property name="invisible_char">*</property>
+			  <property name="activates_default">False</property>
+			  <signal name="activate" handler="HandleTagEntryActivate" last_modification_time="Mon, 07 Nov 2005 20:18:18 GMT"/>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="tag_close_button">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="relief">GTK_RELIEF_NONE</property>
+			  <property name="focus_on_click">True</property>
+			  <signal name="pressed" handler="HandleTagBarCloseButtonPressed" last_modification_time="Tue, 08 Nov 2005 01:22:39 GMT"/>
+
+			  <child>
+			    <widget class="GtkImage" id="image23">
+			      <property name="visible">True</property>
+			      <property name="stock">gtk-close</property>
+			      <property name="icon_size">4</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">0</property>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </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>
@@ -7671,6 +7753,7 @@
 	  <property name="padding">0</property>
 	  <property name="expand">False</property>
 	  <property name="fill">False</property>
+	  <property name="pack_type">GTK_PACK_END</property>
 	</packing>
       </child>
     </widget>


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