Re: Tag typing.



Here's v4 of the tag typing patch.  I believe this will be the final
version before committing to CVS.  A number of bugs are fixed in here,
and I added a ChangeLog entry.

The 't' behavior is enabled in here; I don't like it yet but I'm willing
to see if it grows on me.  Aaron's enter-expansion patch is in here too.

If people could please test this and make sure there aren't any
lingering bugs before I commit, that would be appreciated.  And of
course I need Larry's green light, too.

best,
Nat

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/f-spot/ChangeLog,v
retrieving revision 1.1112
diff -u -r1.1112 ChangeLog
--- ChangeLog	8 Nov 2005 18:25:10 -0000	1.1112
+++ ChangeLog	10 Nov 2005 21:18:24 -0000
@@ -1,3 +1,27 @@
+2005-11-10  Nat Friedman  <nat novell com>
+
+	* src/MainWindow.cs: Added support for creating and applying tags
+	by typing them into a tagbar entry.  Added support for merging
+	tags.  Support for multiselection operations in the tag sidebar.
+	Handle photo deleting in the icon and photo views by catching the
+	keypress in the icon and photo view widgets, not as a menu
+	accelerator.  Pressing delete in the tag treeview will now delete
+	the selected tags.  Fixed several bugs.
+
+	* src/TagPopup.cs: Added support for merging tags.  Added support
+	for multiselection tag actions.
+
+	* src/TagSelectionWidget.cs: Handle top-level tag creation.
+
+	* src/Db.cs: Made the item_cache protected so the TagStore
+	subclass can access it.
+
+	* src/TagStore.cs: Added GetTagByName and GetTagByNameStart, both
+	of which are case-insensitive.
+
+	* src/f-spot.glade: Remove the Delete accelerator for deleting
+	photos.  Add the tag typing bar.
+
 2005-11-08  Larry Ewing  <lewing novell com>
 
 	* src/Loupe.cs: queue a resize iff the dimentions have changed so that the
Index: src/Db.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/Db.cs,v
retrieving revision 1.7
diff -u -r1.7 Db.cs
--- src/Db.cs	7 Oct 2005 21:35:39 -0000	1.7
+++ src/Db.cs	10 Nov 2005 21:18:24 -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: src/MainWindow.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/MainWindow.cs,v
retrieving revision 1.228
diff -u -r1.228 MainWindow.cs
--- src/MainWindow.cs	2 Nov 2005 08:33:16 -0000	1.228
+++ src/MainWindow.cs	10 Nov 2005 21:18:25 -0000
@@ -15,6 +15,7 @@
 using LibGPhoto2;
 
 public class MainWindow {
+
         public static MainWindow Toplevel;
 
 	Db db;
@@ -83,6 +84,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 +177,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 +256,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 +266,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 +286,8 @@
 		UpdateMenus ();
 		main_window.ShowAll ();
 
+		tagbar.Hide ();
+
 		LoadPreference (Preferences.SHOW_TOOLBAR);
 		LoadPreference (Preferences.SHOW_SIDEBAR);
 		LoadPreference (Preferences.SHOW_TIMELINE);
@@ -455,7 +470,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 +519,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 +636,8 @@
 
 			break;
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 
 #if SHOW_CALENDAR
@@ -789,6 +801,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 +843,8 @@
 					AttachTags (tag_selection_widget.TagHighlight (), SelectedIds());
 				else 
 					AttachTags (tag_selection_widget.TagHighlight (), new int [] {item});
+
+				UpdateTagEntryFromSelection ();
 			}
 			break;
 		case (uint)TargetType.UriList:
@@ -851,7 +872,9 @@
 		info_box.Photo = CurrentPhoto;
 		if (info_display != null)
 			info_display.Photo = CurrentPhoto;
+
 		UpdateMenus ();
+		UpdateTagEntryFromSelection ();
 	}
 
 	void HandleDoubleClicked (IconView icon_view, int clicked_item)
@@ -870,6 +893,7 @@
 		if (info_display != null)
 			info_display.Photo = CurrentPhoto;
 		UpdateMenus ();
+		UpdateTagEntryFromSelection ();
 	}
 	
 	void HandlePhotoViewKeyPressEvent (object sender, Gtk.KeyPressEventArgs args)
@@ -884,6 +908,10 @@
 			SetViewMode (ModeType.IconView);
 			args.RetVal = true;
 			break;
+		case Gdk.Key.Delete:
+			HandleRemoveCommand (sender, (EventArgs) args);
+			args.RetVal = true;
+			break;
 		default:
 			break;
 		}
@@ -960,6 +988,8 @@
 		foreach (int num in SelectedIds ()) {
 			AddTagExtended (num, new Tag [] {t});
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 	
 	void HandleFindTagMenuSelected (Tag t)
@@ -973,6 +1003,8 @@
 			query.Photos [num].RemoveTag (t);
 			query.Commit (num);
 		}
+
+		UpdateTagEntryFromSelection ();
 	}
 
 	//
@@ -1338,16 +1370,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 +1772,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 +2084,264 @@
 		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;
+
+#if !ALLOW_TAG_TYPING_WITHOUT_HOTKEY
+		if (args.Event.Key != Gdk.Key.t)
+			return;
+#endif
 
+#if ALLOW_TAG_TYPING_WITHOUT_HOTKEY
+		char c = System.Convert.ToChar (Gdk.Keyval.ToUnicode ((uint) args.Event.Key));
+		if (! System.Char.IsLetter (c))
+			return;
+#endif
+		
+		if (tag_entry.Text.Length > 0)
+			tag_entry.Text += ", ";
+
+#if ALLOW_TAG_TYPING_WITHOUT_HOTKEY
+		tag_entry.Text += c;
+#endif
+
+		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;
+
+               int sel_start, sel_end;
+               if (tag_entry.GetSelectionBounds (out sel_start, out sel_end) && tag_completion_index != -1) {
+                       tag_entry.InsertText (", ", ref sel_end);
+                       tag_entry.SelectRegion (-1, -1);
+                       tag_entry.Position = sel_end + 2;
+                       ClearTagCompletions ();
+                       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.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.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: src/TagPopup.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagPopup.cs,v
retrieving revision 1.3
diff -u -r1.3 TagPopup.cs
--- src/TagPopup.cs	22 Oct 2005 06:10:27 -0000	1.3
+++ src/TagPopup.cs	10 Nov 2005 21:18:25 -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: src/TagSelectionWidget.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagSelectionWidget.cs,v
retrieving revision 1.21
diff -u -r1.21 TagSelectionWidget.cs
--- src/TagSelectionWidget.cs	2 Nov 2005 05:35:57 -0000	1.21
+++ src/TagSelectionWidget.cs	10 Nov 2005 21:18:26 -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: src/TagStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagStore.cs,v
retrieving revision 1.20
diff -u -r1.20 TagStore.cs
--- src/TagStore.cs	28 Oct 2005 19:52:30 -0000	1.20
+++ src/TagStore.cs	10 Nov 2005 21:18:26 -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: src/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
--- src/f-spot.glade	7 Nov 2005 20:06:48 -0000	1.115
+++ src/f-spot.glade	10 Nov 2005 21:18:27 -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]