Hey everyone, 

the attached patch gives the docbrowser persistent hierarchical
bookmarks. You can see a sample screenshot here:
http://www.ophion.org/index.php?gadget=Blog&action=SingleView&id=3

The patch is significantly large since it abstracts all of the bookmark
logic into a BookmarkManager class, revamps the bookmark dialogs and
makes some of the Browser's types (including the browser itself) public.
I humbly believe that this is a first step in the direction of making
the overall code more manageable by breaking up some of the browser.cs
complexity. 

Since I'm not quite the GTK wizard, I'm looking forward to as much
feedback as possible on the dialog boxes and the overall behavior of the
bookmark logic.

Cheers, 

- raf
Index: browser.cs
===================================================================
--- browser.cs	(revision 50718)
+++ browser.cs	(working copy)
@@ -7,8 +7,6 @@
 // (C) 2003 Ximian, Inc.
 //
 // TODO:
-//   Add support for printing.
-//   Add search facility
 //
 using Gtk;
 using Glade;
@@ -108,7 +106,7 @@
 	}
 }
 
-class Browser {
+public class Browser {
 	Glade.XML ui;
 	Gtk.Window MainWindow;
 	Style bar_style;
@@ -130,7 +128,7 @@
 	bool HoldCtrl;
 	public bool UseGecko;
 
-	[Glade.Widget] MenuItem bookmarksMenu;
+	[Glade.Widget] public MenuItem bookmarksMenu;
 	[Glade.Widget] MenuItem view1;
 	MenuItem textLarger;
 	MenuItem textSmaller;
@@ -145,6 +143,9 @@
 	[Glade.Widget] Box title_label_box;
 	ELabel title_label;
 
+	// Bookmark Manager
+	BookmarkManager bookmark_manager;
+
 	//
 	// Accessed from the IndexBrowser class
 	//
@@ -166,9 +167,9 @@
         //
 	// Left-hand side Browsers
 	//
-	TreeBrowser tree_browser;
+	public TreeBrowser tree_browser;
 	IndexBrowser index_browser;
-	string CurrentUrl;
+	public string CurrentUrl;
 	
 	internal RootTree help_tree;
 
@@ -227,6 +228,9 @@
 		help_tree = RootTree.LoadTree ();
 		tree_browser = new TreeBrowser (help_tree, reference_tree, this);
 		
+		// Bookmark Manager init;
+		bookmark_manager = new BookmarkManager(this);
+		
 		//
 		// Tab Notebook and first tab
 		//
@@ -558,7 +562,7 @@
 			//
 			string tabTitle;
 			tabTitle = matched_node.Caption; //Normal title
-			string[] parts = matched_node.URL.Split('/', '#');			
+			string[] parts = matched_node.URL.Split('/', '#');
 			if(matched_node.URL != null && matched_node.URL.StartsWith("ecma:")) {
 				if(parts.Length == 3 && parts[2] != String.Empty) { //List of Members, properties, events, ...
 					tabTitle = parts[1] + ": " + matched_node.Caption;
@@ -1399,132 +1403,13 @@
 	{
 		AddTab();
 	}
-
-	void BookmarkHandle (object obj, EventArgs args)
-	{
-		Menu aux = (Menu) bookmarksMenu.Submenu;
-		Gtk.Widget [] a = aux.Children;
-		for (int i = 3; i < aux.Children.Length; i++) {
-			if (aux.Children [i] == obj)
-				LoadUrl (((BookLink) bookList [i - 3]).Url);
-		}
-	}
-
-	void refreshBookmarkMenu ()
-	{
-		Menu aux = (Menu) bookmarksMenu.Submenu;
-		
-		foreach (Widget w in aux.Children)
-			aux.Remove (w);
-
-
-		if (bookList.Count > 0) {
-			MenuItem aux2 = new SeparatorMenuItem ();
-			aux2.Show ();
-			aux.Append (aux2);
-
-			for (int i = 0; i < bookList.Count; i++) {
-				aux2 = new MenuItem (((BookLink) bookList [i]).Text);
-				aux2.Activated += new EventHandler (BookmarkHandle);
-				aux2.Show ();
-				aux.Append (aux2);
-			}
-		}
-	}
-
-	void OnAddBookmark (object sender, EventArgs a)
-	{
-//		This url is not secure to 100 percent -> tree_browser.SelectedNode.Element
-//		Console.WriteLine("Example: {0} {1}", CurrentUrl, tree_browser.SelectedNode.Caption);
-		bookList.Add (new BookLink (tree_browser.SelectedNode.Caption, CurrentUrl));
-		refreshBookmarkMenu();
-	}
-
-	void OnEditBookmarks (object sender, EventArgs a)
-	{
-		BookmarkEdit.Show(this);
-	}
-
-	class BookmarkEdit {
-		[Glade.Widget] Window bookmarks_edit;
-		[Glade.Widget] Button bookmarks_delete;
-		[Glade.Widget] TreeView bookmarks_treeview;
-		TreeStore store;
-
-		static BookmarkEdit BookmarkEditBox;
-		Browser parent;
-
-		BookmarkEdit (Browser browser)
-                {
-			Glade.XML ui = new Glade.XML (null, "browser.glade", "bookmarks_edit", null);
-			ui.Autoconnect (this);
-			parent = browser;
-			bookmarks_edit.TransientFor = parent.window1;
-			bookmarks_delete.Sensitive = false;
-
-			store = new TreeStore (typeof (string), typeof (int));
-			bookmarks_treeview.AppendColumn ("name_col", new CellRendererText (), "text", 0);
-			bookmarks_treeview.Model = store;
-
-			Load ();
-		}
-
-		static public void Show (Browser browser)
-		{
-			if (BookmarkEditBox == null)
-				BookmarkEditBox = new BookmarkEdit (browser);
-
-			BookmarkEditBox.Load ();
-			BookmarkEditBox.bookmarks_edit.Show ();
-		}
-
-		void OnCancelClicked (object sender, EventArgs a)
-		{
-			bookmarks_edit.Hide ();
-		}
-
-		void OnDeleteClicked (object sender, EventArgs a)
-		{
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model;
-
-			if (bookmarks_treeview.Selection.GetSelected (out model, out iter))
-				parent.bookList.RemoveAt((int)model.GetValue(iter,1));
-
-			if (parent.bookList.Count==0)
-				bookmarks_delete.Sensitive = false;
-
-			Load ();
-			parent.refreshBookmarkMenu();
-		}
-
-		public void AppendItem (string text, int num)
-		{
-			store.AppendValues (text, num);
-			bookmarks_delete.Sensitive = true;
-		}
-
-		public void Load ()
-		{
-			store.Clear ();
-			for (int i = 0; i < parent.bookList.Count; i++)
-				AppendItem (((BookLink) parent.bookList[i]).Text, i);
-		}
-
-		//
-		// Called on the Window delete icon clicked
-		//
-		void OnDelete (object sender, EventArgs a)
-		{
-			BookmarkEditBox = null;
-		}
-	}
+	
 }
 
 //
 // This class implements the tree browser
 //
-class TreeBrowser {
+public class TreeBrowser {
 	Browser browser;
 
 	TreeView tree_view;
@@ -2057,7 +1942,7 @@
 //
 // A Tab is a Notebok with two pages, one for editing and one for visualizing
 //
-class Tab : Notebook {
+public class Tab : Notebook {
 	
 	// Our HTML preview during editing.
 	public IHtmlRender html_preview;
@@ -2082,6 +1967,12 @@
 	public System.Xml.XmlNode edit_node;
 	public string edit_url;
 	
+	void FocusOut (object sender, FocusOutEventArgs args)
+	{
+		if (TabMode == Mode.Editor)
+			text_editor.GrabFocus ();	
+	}
+
 	public Tab(Browser br) 
 	{
 
@@ -2153,6 +2044,7 @@
 		text_editor.Buffer.Changed += new EventHandler (EditedTextChanged);
 		text_editor.WrapMode = WrapMode.Word;
 		sw.Add(text_editor);
+		text_editor.FocusOutEvent += new FocusOutEventHandler (FocusOut);
 		
 		//
 		// XML editing buttons
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 50718)
+++ ChangeLog	(working copy)
@@ -1,3 +1,15 @@
+2005-11-01  Rafael Ferreira <[EMAIL PROTECTED]>
+	* browser.cs: 
+		- Changes to make the browser a public object,
+		removed old bookmark logic
+	* BookmarkManager.cs:
+		- Manages all of the bookmark logic (new)
+	* browser.glade:
+		- removed edit_bookmark, added manage_bookmarks_dialog 
+		and add_bookmark_dialog
+	* Makefile.am:
+		- added BookmarkManager.cs
+		
 2005-09-12  Miguel de Icaza  <[EMAIL PROTECTED]>
 
 	* browser.cs (EditedTextChanged): Only update the preview every
Index: browser.glade
===================================================================
--- browser.glade	(revision 50718)
+++ browser.glade	(working copy)
@@ -261,23 +261,7 @@
 	      <child>
 		<widget class="GtkMenu" id="bookmarksMenu_menu">
 
-		  <child>
-		    <widget class="GtkMenuItem" id="add_bookmark1">
-		      <property name="visible">True</property>
-		      <property name="label" translatable="yes">_Add bookmark</property>
-		      <property name="use_underline">True</property>
-		      <signal name="activate" handler="OnAddBookmark" last_modification_time="Sat, 13 Dec 2003 16:35:00 GMT"/>
-		    </widget>
-		  </child>
 
-		  <child>
-		    <widget class="GtkMenuItem" id="edit_bookmarks1">
-		      <property name="visible">True</property>
-		      <property name="label" translatable="yes">_Edit bookmarks...</property>
-		      <property name="use_underline">True</property>
-		      <signal name="activate" handler="OnEditBookmarks" last_modification_time="Sun, 14 Dec 2003 16:48:05 GMT"/>
-		    </widget>
-		  </child>
 		</widget>
 	      </child>
 	    </widget>
@@ -3148,11 +3132,11 @@
   </child>
 </widget>
 
-<widget class="GtkWindow" id="bookmarks_edit">
+<widget class="GtkWindow" id="manage_bookmarks_dialog">
   <property name="visible">True</property>
-  <property name="title" translatable="yes">Edit bookmarks</property>
+  <property name="title" translatable="yes">Manage Bookmarks</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
-  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="window_position">GTK_WIN_POS_CENTER</property>
   <property name="modal">True</property>
   <property name="default_width">375</property>
   <property name="default_height">275</property>
@@ -3161,8 +3145,9 @@
   <property name="decorated">True</property>
   <property name="skip_taskbar_hint">False</property>
   <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
   <signal name="delete_event" handler="OnDelete" last_modification_time="Sun, 14 Dec 2003 16:36:48 GMT"/>
 
   <child>
@@ -3195,6 +3180,9 @@
 		  <property name="rules_hint">False</property>
 		  <property name="reorderable">False</property>
 		  <property name="enable_search">True</property>
+		  <property name="fixed_height_mode">False</property>
+		  <property name="hover_selection">False</property>
+		  <property name="hover_expand">False</property>
 		</widget>
 	      </child>
 	    </widget>
@@ -3210,10 +3198,10 @@
 	      <property name="border_width">9</property>
 	      <property name="visible">True</property>
 	      <property name="layout_style">GTK_BUTTONBOX_START</property>
-	      <property name="spacing">0</property>
+	      <property name="spacing">4</property>
 
 	      <child>
-		<widget class="GtkButton" id="bookmarks_delete">
+		<widget class="GtkButton" id="DeleteButton">
 		  <property name="visible">True</property>
 		  <property name="can_default">True</property>
 		  <property name="can_focus">True</property>
@@ -3224,6 +3212,19 @@
 		  <signal name="clicked" handler="OnDeleteClicked" last_modification_time="Sun, 14 Dec 2003 17:13:00 GMT"/>
 		</widget>
 	      </child>
+
+	      <child>
+		<widget class="GtkButton" id="NewFolderButton">
+		  <property name="visible">True</property>
+		  <property name="can_default">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label" translatable="yes">New Folder</property>
+		  <property name="use_underline">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		  <signal name="clicked" handler="on_NewFolderButton_clicked" last_modification_time="Mon, 31 Oct 2005 03:48:31 GMT"/>
+		</widget>
+	      </child>
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>
@@ -3280,5 +3281,181 @@
     </widget>
   </child>
 </widget>
+<widget class="GtkWindow" id="add_bookmark_dialog">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Add Bookmark</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER</property>
+  <property name="modal">True</property>
+  <property name="default_width">60</property>
+  <property name="default_height">115</property>
+  <property name="resizable">False</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <signal name="delete_event" handler="on_AddBookmark_delete_event" last_modification_time="Mon, 26 Sep 2005 04:54:22 GMT"/>
 
+  <child>
+    <widget class="GtkVBox" id="vbox32">
+      <property name="border_width">23</property>
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox36">
+	  <property name="border_width">2</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">22</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="Label67">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Name:</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="name_entry">
+	      <property name="visible">True</property>
+	      <property name="can_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>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox37">
+	  <property name="border_width">2</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">5</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label68">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Create in:</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>
+	    <placeholder/>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox38">
+	  <property name="border_width">5</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">199</property>
+
+	  <child>
+	    <widget class="GtkButton" id="CancelButton">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <signal name="clicked" handler="on_CancelButton_clicked" last_modification_time="Mon, 26 Sep 2005 04:36:50 GMT"/>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="AddButton">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-add</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <signal name="clicked" handler="on_AddButton_clicked" last_modification_time="Mon, 26 Sep 2005 04:36:30 GMT"/>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">4</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
 </glade-interface>
Index: BookmarkManager.cs
===================================================================
--- BookmarkManager.cs	(revision 0)
+++ BookmarkManager.cs	(revision 0)
@@ -0,0 +1,534 @@
+//
+// Filename BookmarkManager.cs: Manages bookmark saving/restoring and the secondary dialog
+// windows
+// Author:
+//	Rafael Ferreira <[EMAIL PROTECTED]>
+//
+// (C) 2005 Rafael Ferreira
+//
+
+using System;
+using Gtk;
+using Glade;
+using System.Collections;
+using System.Xml.Serialization;
+using System.Xml;
+using System.IO;
+
+namespace Monodoc {
+	public class BookmarkManager {
+
+		[Serializable]
+		public class BookmarkBase {
+			[XmlAttribute]
+			public string ID = Guid.NewGuid().ToString();
+			[XmlAttribute]
+			public string Name = String.Empty;
+			
+			public BookmarkBase() {}
+
+		}
+		[Serializable]
+		[XmlInclude(typeof(Bookmark))]
+		public class BookmarkGroup : BookmarkBase {
+			
+			[XmlElement("Member")]
+			public ArrayList Members = new ArrayList();
+
+			public BookmarkGroup() {}
+
+			public BookmarkGroup(string name) {
+				Name = name;
+
+			}
+		}
+		
+		[Serializable]
+		public  class Bookmark : BookmarkBase {
+
+			public string Url;
+			
+			public Bookmark(string name, string url){
+				Name = name;
+				Url = url;
+			}
+			
+			public Bookmark() {}
+		}
+		internal class ManageBookmarkDialog {
+			[Glade.Widget] Gtk.TreeView bookmarks_treeview;
+			[Glade.Widget] Gtk.Window manage_bookmarks_dialog;
+			BookmarkGroup root_group;
+			Hashtable iter_to_id; 
+			string selected_id = string.Empty;
+			CellRendererText cell_render;
+			const string UNTITLED = "Untitled";
+		
+			public ManageBookmarkDialog(BookmarkGroup bookmarks) {
+				Glade.XML xml = new Glade.XML("browser.glade","manage_bookmarks_dialog");
+				xml.Autoconnect(this);
+				iter_to_id = new Hashtable();
+				root_group = bookmarks;
+				bookmarks_treeview.RulesHint = true;
+				bookmarks_treeview.EnableSearch = true;
+
+				// treeview handlers
+				bookmarks_treeview.RowExpanded += new Gtk.RowExpandedHandler(on_row_expanded);
+				bookmarks_treeview.Selection.Changed += new EventHandler(on_row_selected);
+				bookmarks_treeview.RowActivated += new Gtk.RowActivatedHandler(on_row_activated);
+				cell_render = new CellRendererText();
+				cell_render.Edited += new EditedHandler(on_cellrender_edited);
+				cell_render.Editable = true;
+				
+				bookmarks_treeview.AppendColumn("Column 1", cell_render,"text",0);
+
+				
+			}
+			
+			void on_row_expanded(object sender, Gtk.RowExpandedArgs args) {
+			}
+			void on_row_selected(object sender, EventArgs args) {
+				Gtk.TreeIter iter;
+				Gtk.TreeModel model;
+				
+				if (bookmarks_treeview.Selection.GetSelected (out model, out iter)) {
+					selected_id = iter_to_id[iter] as string;
+				}
+
+			}
+			
+			void on_cellrender_edited(object sender, EditedArgs args) {
+				
+				// root group can't be edited
+				if ( selected_id == root_group.ID)
+					return;
+
+				BookmarkBase bk = null;
+				BookmarkManager.GetBookmarkBase(root_group,selected_id,ref bk);
+
+				if (bk == null ) {
+					Console.WriteLine("error, could not retrieve bookmark:{0}",selected_id);
+					return;
+				}
+				
+				// it is not a bookmark
+				bk.Name = args.NewText;
+				
+				
+				//refreshing tree_view
+				BookmarkManager.Refresh();
+				BuildTreeView();
+			}
+			
+			void on_row_activated(object sender, Gtk.RowActivatedArgs args) {
+			}
+			void OnDelete(object o, EventArgs args) {
+				manage_bookmarks_dialog.Destroy();
+			}
+			void on_NewFolderButton_clicked(object sender, EventArgs args) {
+				BookmarkManager.AddBookmarkGroup(root_group,selected_id,UNTITLED);
+				BookmarkManager.Refresh();
+				BuildTreeView();
+			}
+			void on_EditButton_clicked(object sender, EventArgs args) {
+			}
+			void OnDeleteClicked(object o, EventArgs args){
+				if (selected_id != string.Empty) {
+					BookmarkManager.DeleteBookmarkBase(root_group,selected_id);
+					BookmarkManager.Refresh();
+					BuildTreeView();
+					
+				}
+
+			}
+			void OnCancelClicked(object o, EventArgs args) {
+				//TODO add undo logic
+				manage_bookmarks_dialog.Hide();
+			}
+
+			public void Show(){
+				BuildTreeView();
+				manage_bookmarks_dialog.ShowAll();
+			}
+
+			void BuildTreeView() {
+				
+
+				TreeStore store = new TreeStore(typeof(string));
+				bookmarks_treeview.Model = store;
+
+				TreeIter iter = store.AppendValues(root_group.Name);
+
+				// appending root
+				iter_to_id[iter] = root_group.ID;
+
+				// calling the recursevily builder
+				BuildTreeViewHelper(root_group,iter,store);
+
+				/*
+				foreach(object i in root_group.Members) {
+					if (i is Bookmark)
+						iter_to_id[store.AppendValues(iter,((Bookmark)i).Name )] = ((Bookmark)i).ID;
+				}
+				*/
+				bookmarks_treeview.ExpandAll();
+			}
+			void BuildTreeViewHelper(BookmarkGroup bookmarks, TreeIter iter, TreeStore store) {
+				TreeIter tmp_iter;
+				
+				foreach (object i in bookmarks.Members) {
+					if (i is BookmarkGroup) {
+						tmp_iter = store.AppendValues(iter,((BookmarkGroup)i).Name );
+						iter_to_id[tmp_iter] = ((BookmarkGroup)i).ID;
+						BuildTreeViewHelper( (BookmarkGroup)i, tmp_iter, store);
+					}
+					if (i is Bookmark) {
+						tmp_iter = store.AppendValues(iter,((Bookmark)i).Name);
+						iter_to_id[tmp_iter] = ((Bookmark)i).ID;
+					}
+				}
+
+			}
+			
+		}
+		internal class AddBookmarkDialog {
+			[Glade.Widget] Gtk.Entry name_entry;
+			[Glade.Widget] HBox hbox37;
+			[Glade.Widget] Gtk.Window add_bookmark_dialog;
+
+			string text, url;
+			BookmarkGroup root;
+			Combo combo;
+			Hashtable combo_to_id = new Hashtable();
+			
+			public AddBookmarkDialog(BookmarkGroup root_group) {
+
+				Glade.XML xml = new Glade.XML("browser.glade","add_bookmark_dialog");
+				xml.Autoconnect(this);
+				
+				combo = new Combo();
+
+				ArrayList list = new ArrayList();
+				
+				BuildComboList(root_group,list);
+				combo.PopdownStrings =  list.ToArray(typeof(string)) as string[];
+				combo.AllowEmpty = false;
+				combo.Entry.Editable = false;
+				combo.DisableActivate();
+				
+				// pusihing widget into hbox
+				hbox37.PackEnd(combo);
+				
+				//combo.Entry.Activated += new EventHandler(on_combo_entry_activated);
+
+				root = root_group;
+				text = url = String.Empty;
+				
+			}
+
+			//recursively builds combo box
+			private void BuildComboList(BookmarkGroup bookmarks, ArrayList list) {
+
+				foreach (object i in bookmarks.Members)
+				{
+					if (i is BookmarkGroup) {
+						BuildComboList(i as BookmarkGroup, list);
+					}
+					
+				}
+				list.Add(bookmarks.Name);
+				combo_to_id[bookmarks.Name] = bookmarks.ID;
+				
+			}
+			public void on_AddBookmark_delete_event(object o, EventArgs args) {
+				add_bookmark_dialog.Destroy();
+			}
+
+			public void on_AddButton_clicked(object o, EventArgs args) {
+				
+				/*
+				if (combo.Entry.Text != text )
+					BookmarkManager.AddBookmarkGroup(root,root.Name,combo.Entry.Text);
+				*/
+			
+				BookmarkManager.AddBookmark(root,combo_to_id[combo.Entry.Text] as string,name_entry.Text,url);
+				add_bookmark_dialog.Hide();
+				BookmarkManager.Refresh();
+			}
+
+			public void on_CancelButton_clicked(object o, EventArgs args) {
+				add_bookmark_dialog.Hide();
+			}
+
+			public void on_combo_entry_activated(object sender, EventArgs args) {
+				
+			}
+			
+			public void Show(string T, string U) {
+				name_entry.Text = T;
+				text = T.Trim();
+				url = U.Trim();
+				add_bookmark_dialog.ShowAll();
+			}
+
+		}
+		
+		// attributes:
+		static BookmarkGroup root_group;
+		static string bookmark_file;
+		static XmlSerializer serializer;
+		static Browser _Browser;
+		static string current_bookmark_group = String.Empty;
+		static AddBookmarkDialog add_window = null;
+		static ManageBookmarkDialog edit_window = null;
+		
+		static Hashtable menu_to_id;
+		
+		const string ADD_BANNER = " Bookmark this page";
+		const string EDIT_BANNER = " Manage bookmarks";
+		const string ROOT_NAME = "Bookmarks";
+
+		private static void Refresh() {
+			BookmarkManager.Save();
+			BookmarkManager.BuildMenu(_Browser.bookmarksMenu);
+		}
+		
+		private static void Save() {
+			using (FileStream file = new FileStream(bookmark_file,FileMode.Create)) {
+				serializer.Serialize(file,root_group);
+			}
+			#if DEBUG
+			Console.WriteLine("bookmarks saved ({0})",root_group.Members.Count);
+			#endif
+		}
+		private static void Load() {
+			using (FileStream file = new FileStream(bookmark_file,FileMode.Open)) {
+				root_group = (BookmarkGroup)serializer.Deserialize(file);
+			}
+			#if DEBUG
+			Console.WriteLine("bookmarks loaded ({0})",root_group.Members.Count);
+			#endif
+		}
+		
+		public BookmarkManager(Browser browser){
+			_Browser = browser;
+			
+			
+			Console.WriteLine("Bookmark Manager init");
+
+			//discoverig bookmark file
+			bookmark_file = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+			bookmark_file = System.IO.Path.Combine (bookmark_file, "monodoc");
+			bookmark_file = System.IO.Path.Combine (bookmark_file, "bookmarks.xml");
+
+			// creating serializer 
+			serializer = new XmlSerializer(typeof(BookmarkGroup));
+
+			// trying to load saved bookmarks
+			try {
+				Load();
+			}catch (Exception e) {
+				// no bookmarks found, creating new root
+				root_group = new BookmarkGroup(ROOT_NAME);
+			}
+			
+			current_bookmark_group = ROOT_NAME;
+			menu_to_id = new Hashtable();
+			BuildMenu(_Browser.bookmarksMenu);
+
+		}
+		
+		public static void BuildMenu(MenuItem bookmark_menu) {
+			Menu aux = (Menu) bookmark_menu.Submenu;
+			
+			foreach (Widget w in aux.Children) {
+				aux.Remove(w);
+			}
+			
+			menu_to_id.Clear();
+			
+			//adding Default Items:
+			AccelGroup bk_grp = new AccelGroup();
+			_Browser.window1.AddAccelGroup(bk_grp);
+			
+			ImageMenuItem item;
+			item  = new ImageMenuItem(ADD_BANNER);
+			//item.Image = new Gtk.Image(Stock.Add,IconSize.Menu);
+			item.AddAccelerator("activate",bk_grp,new AccelKey(Gdk.Key.D,Gdk.ModifierType.ControlMask,AccelFlags.Visible));
+			item.Activated += on_add_bookmark_activated;
+			aux.Append(item);
+
+			//edit
+			item = new ImageMenuItem(EDIT_BANNER);
+			item.AddAccelerator("activate",bk_grp,new AccelKey(Gdk.Key.M,Gdk.ModifierType.ControlMask,AccelFlags.Visible));
+			item.Activated += on_edit_bookmark_activated;
+			//item.Image = new Gtk.Image(Stock.Edit,Iconsize,Menu);
+			aux.Append(item);
+			
+			// and finally the separtor
+			aux.Append(new SeparatorMenuItem());
+			
+			BuildMenuHelper(aux,root_group);
+			aux.ShowAll();
+		}
+		
+		private static void BuildMenuHelper(Menu menu, BookmarkGroup group) {
+			foreach (object i in group.Members)
+			{
+
+				if (i is BookmarkGroup) {
+					MenuItem item = new MenuItem( ((BookmarkGroup)i).Name);
+					item.Activated += on_bookmarkgroup_activated;
+					menu_to_id[item] = ((BookmarkGroup)i).ID;
+					menu.Append(item);
+					Menu m = new Menu();
+					item.Submenu = m;
+					BuildMenuHelper(m,((BookmarkGroup)i));
+				}
+			}
+			
+			foreach (object i in group.Members) 
+			{
+				if (i is Bookmark) {
+					#if DEBUG
+					Console.WriteLine("appending bookmark: [" + ((Bookmark)i).Name + "]");
+					#endif
+
+					MenuItem item = new MenuItem( ((Bookmark)i).Name);
+					menu_to_id[item] = ((Bookmark)i).ID;
+					item.Activated += on_bookmark_activated;
+					menu.Append(item);
+					menu_to_id[item] = ((Bookmark)i).ID;
+				}
+			}
+		}
+		
+		// Event Handlers
+		static void on_add_bookmark_activated(object sender, EventArgs e){
+			add_window = new AddBookmarkDialog(root_group);
+			add_window.Show(_Browser.CurrentTab.Title,_Browser.CurrentUrl);
+		}
+		
+		static void on_edit_bookmark_activated(object sender,EventArgs e) {
+			edit_window = new ManageBookmarkDialog(root_group);
+			edit_window.Show();
+		}
+
+		static void on_bookmark_activated(object sender, EventArgs e) {
+
+			// finding the inner label
+			BookmarkBase bk = null;
+			#if DEBUG
+			Console.WriteLine("looking for {0}/{1}",current_bookmark_group,label.Name);
+			#endif
+			
+			GetBookmarkBase(root_group, menu_to_id[(MenuItem)sender] as string,ref  bk);
+	
+			if (bk != null) {
+				if (bk is  Bookmark)
+					try {
+						_Browser.LoadUrl( ((Bookmark)bk).Url);
+					} catch (Exception ex) {
+						Console.WriteLine("##########################");
+						Console.WriteLine("Exception loading bookmark:[{0}],",((Bookmark)bk).Url);
+						Console.WriteLine("##########################");
+						Console.WriteLine(ex);
+					}
+			}
+			else {
+				Console.WriteLine("Bookmark error -> could not load bookmark");
+			}
+		}
+
+		static void on_bookmarkgroup_activated(object sender, EventArgs e) {
+			// finding the inner label
+			if ( ((MenuItem)sender).Child is Gtk.Label)  {
+				Gtk.Label label = (Gtk.Label) ((MenuItem)sender).Child;
+				current_bookmark_group = label.Name;
+			}
+		}
+		
+		public void EditBookMark() {}
+
+		// static helper methods
+		/// <summary> Recursively deletes a bookmark </summary>
+		public static void DeleteBookmarkBase(BookmarkGroup bookmarks, string ID) {
+		
+			foreach (object i in bookmarks.Members) {
+			
+				if ( i is Bookmark) {
+					if ( ((Bookmark)i).ID == ID) {
+						bookmarks.Members.Remove(i);
+						return;
+					}
+					
+				}else if (i is BookmarkGroup) {
+					if ( ((BookmarkGroup)i).ID == ID) {
+						bookmarks.Members.Remove(i);
+						return;
+					}
+					DeleteBookmarkBase( ((BookmarkGroup)i),ID);
+				}
+
+				
+			}
+		}
+		
+		/// <summary> Recursively finds a bookmarkbase </summary>
+		public static void GetBookmarkBase(BookmarkGroup bookmarks, string ID, ref BookmarkBase retval) {
+			
+			foreach (object i in bookmarks.Members) {
+
+				if ( ((BookmarkBase)i).ID == ID) {
+					retval = i as BookmarkBase;
+					return;
+				}
+				
+				if ( i is BookmarkGroup) {
+					GetBookmarkBase((BookmarkGroup)i,ID,ref retval);
+				}
+				
+				
+			}
+		}
+
+		/// <summary> Recursively adds a bookmark </summary>
+		public static void AddBookmark(BookmarkGroup bookmarks, string parent_ID, string bookmark_text, string bookmark_url) {
+			if ( bookmarks.ID == parent_ID) {
+				bookmarks.Members.Add(new Bookmark(bookmark_text,bookmark_url) );
+				return;
+			}
+			foreach (object i in bookmarks.Members)
+			{
+				if (i is BookmarkGroup) {
+					AddBookmark( ((BookmarkGroup)i),parent_ID,bookmark_text,bookmark_url);
+				
+				}
+			}
+		}
+		
+		/// <summary> Recursively adds a bookmark </summary>
+		public static void AddBookmarkGroup(BookmarkGroup bookmarks, string parent_ID, string name) {
+			if ( bookmarks.ID == parent_ID) {
+				bookmarks.Members.Add(new BookmarkGroup(name) );
+				return;
+			}
+			
+			foreach (object i in bookmarks.Members)
+			{
+				if (i is BookmarkGroup) {
+					AddBookmarkGroup( ((BookmarkGroup)i),parent_ID,name);
+				
+				}
+				if (i is Bookmark ){
+					if ( ((Bookmark)i).ID == parent_ID ) {
+						bookmarks.Members.Add(new BookmarkGroup(name));
+						return;
+					}
+				}
+				
+			}
+		}
+	}
+}
+
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 50718)
+++ Makefile.am	(working copy)
@@ -24,7 +24,9 @@
 	$(srcdir)/Contributions.cs	\
 	$(srcdir)/XmlNodeWriter.cs	\
 	$(srcdir)/GtkHtmlHtmlRender.cs	\
-	$(srcdir)/IHtmlRender.cs	
+	$(srcdir)/IHtmlRender.cs	\
+	$(srcdir)/BookmarkManager.cs	
+	
 
 
 geckorender_sources = \
_______________________________________________
Mono-docs-list maillist  -  Mono-docs-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-docs-list

Reply via email to