vinagre r533 - in trunk: . data po src



Author: jwendell
Date: Thu Dec 11 17:40:20 2008
New Revision: 533
URL: http://svn.gnome.org/viewvc/vinagre?rev=533&view=rev

Log:
2008-12-11  Jonh Wendell <jwendell gnome org>

	* lots of files: Folders in bookmarks, closes #549649.
	Sorting entries alphabetically, closes #550596.



Added:
   trunk/src/vinagre-bookmarks-entry.c
   trunk/src/vinagre-bookmarks-entry.h
   trunk/src/vinagre-bookmarks-migration.c
   trunk/src/vinagre-bookmarks-migration.h
   trunk/src/vinagre-bookmarks-tree.c
   trunk/src/vinagre-bookmarks-tree.h
   trunk/src/vinagre-bookmarks-ui.c
   trunk/src/vinagre-bookmarks-ui.h
Removed:
   trunk/src/gossip-cell-renderer-expander.c
   trunk/src/gossip-cell-renderer-expander.h
Modified:
   trunk/ChangeLog
   trunk/configure.ac
   trunk/data/vinagre-ui.xml
   trunk/data/vinagre.glade
   trunk/po/POTFILES.in
   trunk/src/Makefile.am
   trunk/src/vinagre-bookmarks.c
   trunk/src/vinagre-bookmarks.h
   trunk/src/vinagre-commands.c
   trunk/src/vinagre-commands.h
   trunk/src/vinagre-fav.c
   trunk/src/vinagre-fav.h
   trunk/src/vinagre-mdns.c
   trunk/src/vinagre-ui.h
   trunk/src/vinagre-window-private.h
   trunk/src/vinagre-window.c

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Thu Dec 11 17:40:20 2008
@@ -14,6 +14,7 @@
 AM_PROG_CC_STDC
 AC_HEADER_STDC
 AM_PROG_LIBTOOL
+AM_PROG_CC_C_O
 
 GNOME_COMPILE_WARNINGS(yes)
 

Modified: trunk/data/vinagre-ui.xml
==============================================================================
--- trunk/data/vinagre-ui.xml	(original)
+++ trunk/data/vinagre-ui.xml	Thu Dec 11 17:40:20 2008
@@ -54,6 +54,7 @@
     <menu name="BookmarksMenu" action="Bookmarks">
       <menuitem name="BookmarksAddMenu" action="BookmarksAdd"/>
       <separator/>
+      <menuitem name="BookmarksNewFolderMenu" action="BookmarksNewFolder"/>
       <menuitem name="BookmarksEditMenu" action="BookmarksEdit"/>
       <menuitem name="BookmarksDelMenu" action="BookmarksDel"/>
       <separator/>
@@ -77,11 +78,24 @@
     <toolitem action="MachineSendCtrlAltDel"/>
   </toolbar>
 
-  <popup name="FavPopup" action="FavPopupAction">
+  <popup name="FavPopupConn" action="FavPopupActionConn">
     <menuitem action="BookmarksOpen"/>
     <separator/>
     <menuitem action="BookmarksEdit"/>
     <menuitem action="BookmarksDel"/>
+    <separator/>
+    <menuitem action="BookmarksNewFolder"/>
+  </popup>
+
+  <popup name="FavPopupFolder" action="FavPopupActionFolder">
+    <menuitem action="BookmarksEdit"/>
+    <menuitem action="BookmarksDel"/>
+    <separator/>
+    <menuitem action="BookmarksNewFolder"/>
+  </popup>
+
+  <popup name="FavPopupEmpty" action="FavPopupActionEmpty">
+    <menuitem action="BookmarksNewFolder"/>
   </popup>
 
 </ui>

Modified: trunk/data/vinagre.glade
==============================================================================
--- trunk/data/vinagre.glade	(original)
+++ trunk/data/vinagre.glade	Thu Dec 11 17:40:20 2008
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Fri Nov 14 21:12:10 2008 -->
+<!--Generated with glade3 3.4.5 on Wed Dec 10 15:09:21 2008 -->
 <glade-interface>
   <widget class="GtkDialog" id="connect_dialog">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -451,146 +451,16 @@
       </widget>
     </child>
   </widget>
-  <widget class="GtkDialog" id="add_to_bookmarks_dialog">
-    <property name="width_request">380</property>
+  <widget class="GtkDialog" id="bookmarks_add_edit_conn_dialog">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
     <property name="border_width">5</property>
-    <property name="title" translatable="yes">Saving to bookmarks</property>
-    <property name="resizable">False</property>
+    <property name="title" translatable="yes">Bookmarks</property>
     <property name="modal">True</property>
     <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
     <property name="destroy_with_parent">True</property>
     <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
     <property name="has_separator">False</property>
     <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox3">
-        <property name="visible">True</property>
-        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkVBox" id="vbox2">
-            <property name="visible">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="spacing">2</property>
-            <child>
-              <widget class="GtkLabel" id="label8">
-                <property name="visible">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Enter a name for this connection&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="padding">6</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="hbox3">
-                <property name="visible">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="spacing">6</property>
-                <child>
-                  <widget class="GtkLabel" id="label10">
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="xalign">0</property>
-                    <property name="label">  </property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label9">
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="label" translatable="yes">_Name:</property>
-                    <property name="use_underline">True</property>
-                    <property name="mnemonic_widget">bookmark_name_entry</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="padding">3</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="bookmark_name_entry">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="activates_default">True</property>
-                  </widget>
-                  <packing>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="padding">6</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area3">
-            <property name="visible">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="layout_style">GTK_BUTTONBOX_END</property>
-            <child>
-              <widget class="GtkButton" id="button5">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label">gtk-cancel</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button6">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="has_default">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label">gtk-ok</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">-5</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkDialog" id="edit_bookmark_dialog">
-    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">Editing a bookmark</property>
-    <property name="resizable">False</property>
-    <property name="modal">True</property>
-    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
-    <property name="destroy_with_parent">True</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <child internal-child="vbox">
       <widget class="GtkVBox" id="dialog-vbox4">
         <property name="visible">True</property>
         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -600,7 +470,7 @@
             <property name="visible">True</property>
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <property name="xalign">0</property>
-            <property name="label" translatable="yes">&lt;b&gt;Editing a bookmark&lt;/b&gt;</property>
+            <property name="label" translatable="yes">&lt;b&gt;Connection&lt;/b&gt;</property>
             <property name="use_markup">True</property>
           </widget>
           <packing>
@@ -627,19 +497,25 @@
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="label">  </property>
               </widget>
+              <packing>
+                <property name="x_options"></property>
+                <property name="y_options"></property>
+              </packing>
             </child>
             <child>
               <widget class="GtkLabel" id="label14">
                 <property name="visible">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="xalign">0</property>
-                <property name="label" translatable="yes">_Bookmark name:</property>
+                <property name="label" translatable="yes">_Name:</property>
                 <property name="use_underline">True</property>
                 <property name="mnemonic_widget">edit_bookmark_name_entry</property>
               </widget>
               <packing>
                 <property name="left_attach">1</property>
                 <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
               </packing>
             </child>
             <child>
@@ -656,6 +532,8 @@
                 <property name="right_attach">2</property>
                 <property name="top_attach">1</property>
                 <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
               </packing>
             </child>
             <child>
@@ -689,6 +567,8 @@
             </child>
           </widget>
           <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
             <property name="position">3</property>
           </packing>
         </child>
@@ -697,7 +577,7 @@
             <property name="visible">True</property>
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <property name="xalign">0</property>
-            <property name="label" translatable="yes">&lt;b&gt;Connection options&lt;/b&gt;</property>
+            <property name="label" translatable="yes">&lt;b&gt;Options&lt;/b&gt;</property>
             <property name="use_markup">True</property>
           </widget>
           <packing>
@@ -779,9 +659,48 @@
             </child>
           </widget>
           <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
             <property name="position">5</property>
           </packing>
         </child>
+        <child>
+          <widget class="GtkLabel" id="label10">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Folder&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">6</property>
+            <property name="position">6</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="folder_box">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="label22">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">    </property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">7</property>
+          </packing>
+        </child>
         <child internal-child="action_area">
           <widget class="GtkHButtonBox" id="dialog-action_area4">
             <property name="visible">True</property>
@@ -945,4 +864,166 @@
       </widget>
     </child>
   </widget>
+  <widget class="GtkDialog" id="bookmarks_add_edit_folder_dialog">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Bookmarks</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox6">
+        <property name="visible">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkLabel" id="label8">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Folder&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">6</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkTable" id="table6">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="n_columns">3</property>
+            <property name="column_spacing">6</property>
+            <property name="row_spacing">6</property>
+            <child>
+              <widget class="GtkLabel" id="label9">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">  </property>
+              </widget>
+              <packing>
+                <property name="x_options"></property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label20">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Name:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">edit_bookmark_folder_name_entry</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="edit_bookmark_folder_name_entry">
+                <property name="width_request">250</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="is_focus">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="activates_default">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label25">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Parent Folder&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">6</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="folder_box1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="label26">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">    </property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">4</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area6">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="button5">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button6">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label">gtk-save</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </glade-interface>

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Thu Dec 11 17:40:20 2008
@@ -11,6 +11,9 @@
 src/vinagre-app.c
 src/vinagre-applet.c
 src/vinagre-bookmarks.c
+src/vinagre-bookmarks-migration.c
+src/vinagre-bookmarks-tree.c
+src/vinagre-bookmarks-ui.c
 src/vinagre-commands.c
 src/vinagre-connect.c
 src/vinagre-connection.c

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Thu Dec 11 17:40:20 2008
@@ -21,23 +21,26 @@
 bin_PROGRAMS = vinagre
 
 vinagre_SOURCES = \
-	vinagre-commands.c vinagre-commands.h		\
-	vinagre-connect.c vinagre-connect.h		\
-	vinagre-connection.c vinagre-connection.h	\
-	vinagre-fav.c vinagre-fav.h			\
-	vinagre-bookmarks.c vinagre-bookmarks.h		\
-	vinagre-main.c 			\
-	vinagre-notebook.c vinagre-notebook.h		\
-	vinagre-tab.c vinagre-tab.h			\
-	vinagre-utils.c vinagre-utils.h			\
-	vinagre-window.c vinagre-window.h		\
-	vinagre-ui.h vinagre-window-private.h		\
-	vinagre-enums.h vinagre-enums.c			\
-	gossip-cell-renderer-expander.c gossip-cell-renderer-expander.h \
-	vinagre-prefs.h vinagre-prefs.c \
-	bacon-message-connection.h bacon-message-connection.c \
-	vinagre-app.h vinagre-app.c \
-	vinagre-bacon.h vinagre-bacon.c \
+	vinagre-commands.c vinagre-commands.h			\
+	vinagre-connect.c vinagre-connect.h			\
+	vinagre-connection.c vinagre-connection.h		\
+	vinagre-fav.c vinagre-fav.h				\
+	vinagre-bookmarks.c vinagre-bookmarks.h			\
+	vinagre-main.c 						\
+	vinagre-notebook.c vinagre-notebook.h			\
+	vinagre-tab.c vinagre-tab.h				\
+	vinagre-utils.c vinagre-utils.h				\
+	vinagre-window.c vinagre-window.h			\
+	vinagre-ui.h vinagre-window-private.h			\
+	vinagre-enums.h vinagre-enums.c				\
+	vinagre-prefs.h vinagre-prefs.c				\
+	bacon-message-connection.h bacon-message-connection.c	\
+	vinagre-app.h vinagre-app.c 				\
+	vinagre-bacon.h vinagre-bacon.c 			\
+	vinagre-bookmarks-entry.h vinagre-bookmarks-entry.c	\
+	vinagre-bookmarks-tree.h vinagre-bookmarks-tree.c	\
+	vinagre-bookmarks-ui.h vinagre-bookmarks-ui.c		\
+	vinagre-bookmarks-migration.h vinagre-bookmarks-migration.c \
 	$(NULL)
 
 if AVAHI
@@ -78,12 +81,15 @@
 	$(AVAHI_CFLAGS)			\
 	$(NULL)
 
-vinagre_applet_SOURCES =		\
-	vinagre-applet.c		\
-	vinagre-bookmarks.h vinagre-bookmarks.c		\
-	vinagre-connection.h vinagre-connection.c	\
-	vinagre-utils.h vinagre-utils.c			\
-	vinagre-enums.h vinagre-enums.c			\
+vinagre_applet_SOURCES =					\
+	vinagre-applet.c					\
+	vinagre-bookmarks.h vinagre-bookmarks.c			\
+	vinagre-connection.h vinagre-connection.c		\
+	vinagre-utils.h vinagre-utils.c				\
+	vinagre-enums.h vinagre-enums.c				\
+	vinagre-bookmarks-entry.h vinagre-bookmarks-entry.c	\
+	vinagre-bookmarks-tree.h vinagre-bookmarks-tree.c	\
+	vinagre-bookmarks-migration.h vinagre-bookmarks-migration.c \
 	$(NULL)
 
 if AVAHI

Added: trunk/src/vinagre-bookmarks-entry.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-entry.c	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,300 @@
+/*
+ * vinagre-bookmarks-entry.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "vinagre-bookmarks-entry.h"
+
+struct _VinagreBookmarksEntryPrivate
+{
+  VinagreBookmarksEntryNode node;
+  VinagreConnection        *conn;
+  gchar                    *name;
+  GSList                   *children; // array of VinagreBookmarksEntry
+  VinagreBookmarksEntry    *parent;
+};
+
+G_DEFINE_TYPE (VinagreBookmarksEntry, vinagre_bookmarks_entry, G_TYPE_OBJECT);
+
+static void
+vinagre_bookmarks_entry_init (VinagreBookmarksEntry *entry)
+{
+  entry->priv = G_TYPE_INSTANCE_GET_PRIVATE (entry, VINAGRE_TYPE_BOOKMARKS_ENTRY, VinagreBookmarksEntryPrivate);
+
+  entry->priv->node = VINAGRE_BOOKMARKS_ENTRY_NODE_INVALID;
+  entry->priv->conn = NULL;
+  entry->priv->name = NULL;
+  entry->priv->children = NULL;
+  entry->priv->parent = NULL;
+}
+
+static void
+vinagre_bookmarks_entry_clear (VinagreBookmarksEntry *entry)
+{
+  if (entry->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+    {
+	g_free (entry->priv->name);
+	g_slist_free (entry->priv->children);
+    }
+}
+
+static void
+vinagre_bookmarks_entry_finalize (GObject *object)
+{
+  VinagreBookmarksEntry *entry = VINAGRE_BOOKMARKS_ENTRY (object);
+
+  if (entry->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+    {
+	g_free (entry->priv->name);
+	g_slist_free (entry->priv->children);
+    }
+
+  G_OBJECT_CLASS (vinagre_bookmarks_entry_parent_class)->finalize (object);
+}
+
+static void
+vinagre_bookmarks_entry_dispose (GObject *object)
+{
+  VinagreBookmarksEntry *entry = VINAGRE_BOOKMARKS_ENTRY (object);
+
+  switch (entry->priv->node)
+    {
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	if (entry->priv->conn)
+	  {
+	    g_object_unref (entry->priv->conn);
+	    entry->priv->conn = NULL;
+	  }
+	break;
+
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	if (entry->priv->children)
+	  {
+	    g_slist_foreach (entry->priv->children, (GFunc) g_object_unref, NULL);
+	    entry->priv->children = NULL;
+	  }
+	break;
+
+      default:
+	g_assert_not_reached ();
+    }
+
+  G_OBJECT_CLASS (vinagre_bookmarks_entry_parent_class)->dispose (object);
+}
+
+static void
+vinagre_bookmarks_entry_class_init (VinagreBookmarksEntryClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (VinagreBookmarksEntryPrivate));
+
+  object_class->finalize = vinagre_bookmarks_entry_finalize;
+  object_class->dispose  = vinagre_bookmarks_entry_dispose;
+}
+
+VinagreBookmarksEntry *
+vinagre_bookmarks_entry_new_folder (const gchar *name)
+{
+  VinagreBookmarksEntry *entry;
+
+  g_return_val_if_fail (name != NULL, NULL);
+
+  entry = VINAGRE_BOOKMARKS_ENTRY (g_object_new (VINAGRE_TYPE_BOOKMARKS_ENTRY, NULL));
+  vinagre_bookmarks_entry_set_node (entry, VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER);
+  vinagre_bookmarks_entry_set_name (entry, name);
+
+  return entry;
+}
+
+VinagreBookmarksEntry *
+vinagre_bookmarks_entry_new_conn (VinagreConnection *conn)
+{
+  VinagreBookmarksEntry *entry;
+
+  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), NULL);
+
+  entry = VINAGRE_BOOKMARKS_ENTRY (g_object_new (VINAGRE_TYPE_BOOKMARKS_ENTRY, NULL));
+  vinagre_bookmarks_entry_set_node (entry, VINAGRE_BOOKMARKS_ENTRY_NODE_CONN);
+  vinagre_bookmarks_entry_set_conn (entry, conn);
+
+  return entry;
+
+}
+
+void
+vinagre_bookmarks_entry_set_node (VinagreBookmarksEntry *entry, VinagreBookmarksEntryNode node)
+{
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+
+  if (entry->priv->node == node)
+    return;
+
+  entry->priv->node = node;
+
+  switch (entry->priv->node)
+    {
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	g_free (entry->priv->name);
+	entry->priv->name = NULL;
+	g_slist_foreach (entry->priv->children, (GFunc) g_object_unref, NULL);
+	g_slist_free (entry->priv->children);
+	entry->priv->children = NULL;
+	break;
+
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	if (entry->priv->conn)
+	  {
+	    g_object_unref (entry->priv->conn);
+	    entry->priv->conn = NULL;
+	  }
+	break;
+
+      default:
+	g_assert_not_reached ();
+    }
+}
+
+VinagreBookmarksEntryNode
+vinagre_bookmarks_entry_get_node (VinagreBookmarksEntry *entry)
+{
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), VINAGRE_BOOKMARKS_ENTRY_NODE_INVALID);
+
+  return entry->priv->node;
+}
+
+void
+vinagre_bookmarks_entry_set_conn (VinagreBookmarksEntry *entry, VinagreConnection *conn)
+{
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+  g_return_if_fail (VINAGRE_IS_CONNECTION (conn));
+  g_return_if_fail (entry->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_CONN);
+
+  if (entry->priv->conn != NULL)
+    g_object_unref (entry->priv->conn);
+
+  entry->priv->conn = g_object_ref (conn);
+}
+
+VinagreConnection *
+vinagre_bookmarks_entry_get_conn (VinagreBookmarksEntry *entry)
+{
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), NULL);
+
+  return entry->priv->conn;
+}
+
+void
+vinagre_bookmarks_entry_set_name (VinagreBookmarksEntry *entry, const gchar *name)
+{
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+  g_return_if_fail (entry->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER);
+  g_return_if_fail (name != NULL);
+
+  g_free (entry->priv->name);
+  entry->priv->name = g_strdup (name);
+}
+
+const gchar *
+vinagre_bookmarks_entry_get_name (VinagreBookmarksEntry *entry)
+{
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), NULL);
+
+  return entry->priv->name;
+}
+
+void
+vinagre_bookmarks_entry_add_child (VinagreBookmarksEntry *entry, VinagreBookmarksEntry *child)
+{
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+  g_return_if_fail (entry->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER);
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (child));
+
+  entry->priv->children = g_slist_insert_sorted (entry->priv->children,
+						 child,
+						 (GCompareFunc)vinagre_bookmarks_entry_compare);
+  child->priv->parent = entry;
+}
+
+gboolean
+vinagre_bookmarks_entry_remove_child (VinagreBookmarksEntry *entry,
+				      VinagreBookmarksEntry *child)
+{
+  GSList *l;
+
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), FALSE);
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (child), FALSE);
+
+  if (g_slist_index (entry->priv->children, child) > -1)
+    {
+      entry->priv->children = g_slist_remove (entry->priv->children, child);
+      return TRUE;
+    }
+
+  for (l = entry->priv->children; l; l = l->next)
+    {
+      VinagreBookmarksEntry *e = (VinagreBookmarksEntry *) l->data;
+
+      if (vinagre_bookmarks_entry_get_node (e) != VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+	continue;
+
+      if (vinagre_bookmarks_entry_remove_child (e, child))
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+GSList *
+vinagre_bookmarks_entry_get_children (VinagreBookmarksEntry *entry)
+{
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), NULL);
+
+  return entry->priv->children;
+}
+
+VinagreBookmarksEntry *
+vinagre_bookmarks_entry_get_parent (VinagreBookmarksEntry *entry)
+{
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), NULL);
+
+  return entry->priv->parent;
+}
+
+gint
+vinagre_bookmarks_entry_compare (VinagreBookmarksEntry *a, VinagreBookmarksEntry *b)
+{
+  gchar *name_a, *name_b;
+  gint   result;
+
+  if (a->priv->node != b->priv->node)
+    return a->priv->node - b->priv->node;
+
+  if (a->priv->node == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+    return g_ascii_strcasecmp (a->priv->name, b->priv->name);
+
+  name_a = vinagre_connection_get_best_name (a->priv->conn);
+  name_b = vinagre_connection_get_best_name (b->priv->conn);
+  result = g_ascii_strcasecmp (name_a, name_b);
+  g_free (name_a);
+  g_free (name_b);
+
+  return result;
+}
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-entry.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-entry.h	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,86 @@
+/*
+ * vinagre-bookmarks-entry.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_BOOKMARKS_ENTRY_H__
+#define __VINAGRE_BOOKMARKS_ENTRY_H__
+
+#include <glib-object.h>
+#include "vinagre-connection.h"
+
+G_BEGIN_DECLS
+
+#define VINAGRE_TYPE_BOOKMARKS_ENTRY             (vinagre_bookmarks_entry_get_type ())
+#define VINAGRE_BOOKMARKS_ENTRY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), VINAGRE_TYPE_BOOKMARKS_ENTRY, VinagreBookmarksEntry))
+#define VINAGRE_BOOKMARKS_ENTRY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), VINAGRE_TYPE_BOOKMARKS_ENTRY, VinagreBookmarksEntryClass))
+#define VINAGRE_IS_BOOKMARKS_ENTRY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VINAGRE_TYPE_BOOKMARKS_ENTRY))
+#define VINAGRE_IS_BOOKMARKS_ENTRY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_BOOKMARKS_ENTRY))
+#define VINAGRE_BOOKMARKS_ENTRY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), VINAGRE_TYPE_BOOKMARKS_ENTRY, VinagreBookmarksEntryClass))
+
+typedef struct _VinagreBookmarksEntryClass   VinagreBookmarksEntryClass;
+typedef struct _VinagreBookmarksEntry        VinagreBookmarksEntry;
+typedef struct _VinagreBookmarksEntryPrivate VinagreBookmarksEntryPrivate;
+
+typedef enum {
+  VINAGRE_BOOKMARKS_ENTRY_NODE_INVALID = 0,
+  VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER,
+  VINAGRE_BOOKMARKS_ENTRY_NODE_CONN
+} VinagreBookmarksEntryNode;
+
+struct _VinagreBookmarksEntryClass
+{
+  GObjectClass parent_class;
+};
+
+struct _VinagreBookmarksEntry
+{
+  GObject parent_instance;
+  VinagreBookmarksEntryPrivate *priv;
+};
+
+
+GType vinagre_bookmarks_entry_get_type (void) G_GNUC_CONST;
+
+VinagreBookmarksEntry *		vinagre_bookmarks_entry_new_folder  (const gchar *name);
+VinagreBookmarksEntry *		vinagre_bookmarks_entry_new_conn    (VinagreConnection *conn);
+
+void				vinagre_bookmarks_entry_add_child   (VinagreBookmarksEntry *entry, VinagreBookmarksEntry *child);
+gboolean			vinagre_bookmarks_entry_remove_child(VinagreBookmarksEntry *entry,
+								     VinagreBookmarksEntry *child);
+
+GSList *			vinagre_bookmarks_entry_get_children(VinagreBookmarksEntry *entry);
+
+void				vinagre_bookmarks_entry_set_node    (VinagreBookmarksEntry *entry, VinagreBookmarksEntryNode node);
+VinagreBookmarksEntryNode	vinagre_bookmarks_entry_get_node    (VinagreBookmarksEntry *entry);
+
+void				vinagre_bookmarks_entry_set_conn    (VinagreBookmarksEntry *entry, VinagreConnection *conn);
+VinagreConnection *		vinagre_bookmarks_entry_get_conn    (VinagreBookmarksEntry *entry);
+
+void				vinagre_bookmarks_entry_set_name    (VinagreBookmarksEntry *entry, const gchar *name);
+const gchar *			vinagre_bookmarks_entry_get_name    (VinagreBookmarksEntry *entry);
+
+//void				vinagre_bookmarks_entry_set_parent  (VinagreBookmarksEntry *entry, VinagreBookmarksEntry *parent);
+VinagreBookmarksEntry *		vinagre_bookmarks_entry_get_parent  (VinagreBookmarksEntry *entry);
+
+gint				vinagre_bookmarks_entry_compare     (VinagreBookmarksEntry *a, VinagreBookmarksEntry *b);
+G_END_DECLS
+
+#endif  /* __VINAGRE_BOOKMARKS_ENTRY_H__ */
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-migration.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-migration.c	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,284 @@
+/*
+ * vinagre-bookmarks-migration.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is a temporary hack to migrate the bookmarks from an .ini format to
+ * a XML one. The new format is used in Vinagre 2.25.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <libxml/parser.h>
+#include <libxml/xmlwriter.h>
+
+#include "vinagre-bookmarks-entry.h"
+#include "vinagre-connection.h"
+#include "vinagre-bookmarks-migration.h"
+#include "vinagre-bookmarks.h"
+
+static void
+fill_xml (GSList *list, xmlTextWriter *writer)
+{
+  GSList                *l;
+  VinagreBookmarksEntry *entry;
+  VinagreConnection     *conn;
+
+  for (l = list; l; l = l->next)
+    {
+      entry = (VinagreBookmarksEntry *) l->data;
+      switch (vinagre_bookmarks_entry_get_node (entry))
+	{
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	    xmlTextWriterStartElement (writer, "folder");
+	    xmlTextWriterWriteAttribute (writer, "name", vinagre_bookmarks_entry_get_name (entry));
+
+	    fill_xml (vinagre_bookmarks_entry_get_children (entry), writer);
+	    xmlTextWriterEndElement (writer);
+	    break;
+
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	    conn = vinagre_bookmarks_entry_get_conn (entry);
+
+	    xmlTextWriterStartElement (writer, "item");
+	    xmlTextWriterWriteElement (writer, "name", vinagre_connection_get_name (conn));
+	    xmlTextWriterWriteElement (writer, "host", vinagre_connection_get_host (conn));
+	    xmlTextWriterWriteFormatElement (writer, "port", "%d", vinagre_connection_get_port (conn));
+	    xmlTextWriterWriteFormatElement (writer, "view_only", "%d", vinagre_connection_get_view_only (conn));
+	    xmlTextWriterWriteFormatElement (writer, "scaling", "%d", vinagre_connection_get_scaling (conn));
+	    xmlTextWriterWriteFormatElement (writer, "fullscreen", "%d", vinagre_connection_get_fullscreen (conn));
+
+	    xmlTextWriterEndElement (writer);
+	    break;
+
+	  default:
+	    g_assert_not_reached ();
+	}
+    }
+}
+
+static gboolean
+save_to_file (GSList *entries, const gchar *filename)
+{
+  xmlTextWriter *writer;
+  xmlBuffer     *buf;
+  int            rc;
+  GError        *error;
+  gboolean       result;
+
+  writer = NULL;
+  buf    = NULL;
+  result = FALSE;
+
+  buf = xmlBufferCreate ();
+  if (!buf)
+    {
+      g_warning (_("Error while migrating bookmarks: Failed to create the XML structure"));
+      return FALSE;
+    }
+
+  writer = xmlNewTextWriterMemory(buf, 0);
+  if (!writer)
+    {
+      g_warning (_("Error while migrating bookmarks: Failed to create the XML structure"));
+      goto finalize;
+    }
+
+  rc = xmlTextWriterStartDocument (writer, NULL, "utf-8", NULL);
+  if (rc < 0)
+    {
+      g_warning (_("Error while migrating bookmarks: Failed to initialize the XML structure"));
+      goto finalize;
+    }
+
+  rc = xmlTextWriterStartElement (writer, "vinagre-bookmarks");
+  if (rc < 0)
+    {
+      g_warning (_("Error while migrating bookmarks: Failed to initialize the XML structure"));
+      goto finalize;
+    }
+
+  fill_xml (entries, writer);
+
+  rc = xmlTextWriterEndDocument (writer);
+  if (rc < 0)
+    {
+      g_warning (_("Error while migrating bookmarks: Failed to finalize the XML structure"));
+      goto finalize;
+    }
+
+  error  = NULL;
+  if (!g_file_set_contents (filename,
+			    (const char *) buf->content,
+			    -1,
+			    &error))
+    {
+      g_warning (_("Error while migrating bookmarks: %s"), error?error->message:_("Unknown error"));
+      if (error)
+	g_error_free (error);
+      goto finalize;
+    }
+
+  result = TRUE;
+
+finalize:
+  if (writer)
+    xmlFreeTextWriter (writer);
+  if (buf)
+    xmlBufferFree (buf);
+
+  return result;
+}
+
+static GSList *
+create_list (GKeyFile *kf)
+{
+  GSList *entries;
+  gchar **conns;
+  gsize   length, i;
+
+  entries = NULL;
+  conns = g_key_file_get_groups (kf, &length);
+  for (i=0; i<length; i++)
+    {
+      VinagreConnection *conn;
+      gchar             *s_value;
+      gint               i_value;
+      gboolean           b_value;
+
+      s_value = g_key_file_get_string (kf, conns[i], "host", NULL);
+      if (!s_value)
+        continue;
+
+      conn = vinagre_connection_new ();
+      vinagre_connection_set_name (conn, conns[i]);
+      vinagre_connection_set_host (conn, s_value);
+      g_free (s_value);
+
+      i_value = g_key_file_get_integer (kf, conns[i], "port", NULL);
+      if (i_value == 0)
+        i_value = 5900;
+      vinagre_connection_set_port (conn, i_value);
+
+      b_value = g_key_file_get_boolean (kf, conns[i], "view_only", NULL);
+      vinagre_connection_set_view_only (conn, b_value);
+
+      b_value = g_key_file_get_boolean (kf, conns[i], "fullscreen", NULL);
+      vinagre_connection_set_fullscreen (conn, b_value);
+
+      b_value = g_key_file_get_boolean (kf, conns[i], "scaling", NULL);
+      vinagre_connection_set_scaling (conn, b_value);
+
+      entries = g_slist_insert_sorted  (entries,
+					vinagre_bookmarks_entry_new_conn (conn),
+					(GCompareFunc)vinagre_bookmarks_entry_compare);
+      g_object_unref (conn);
+    }
+
+  g_strfreev (conns);
+  return entries;
+}
+
+static gboolean
+create_dir (const gchar *filename, GError **error)
+{
+  GFile    *file, *parent;
+  gboolean result;
+  gchar    *path;
+
+  file   = g_file_new_for_path (filename);
+  parent = g_file_get_parent (file);
+  path   = g_file_get_path (parent);
+  result = TRUE;
+
+  if (!g_file_test (path, G_FILE_TEST_EXISTS))
+    result = g_file_make_directory_with_parents (parent, NULL, error);
+
+  g_object_unref (file);
+  g_object_unref (parent);
+  g_free (path);
+  return result;
+}
+
+void
+vinagre_bookmarks_migration_migrate (const gchar *filename)
+{
+  gchar    *old;
+  GKeyFile *kf;
+  GError   *error;
+  GSList   *entries;
+
+  error  = NULL;
+  if (!create_dir (filename, &error))
+    {
+      g_warning (_("Error while migrating bookmarks: %s"), error?error->message:_("Failed to create the directory"));
+      if (error)
+	g_error_free (error);
+      return;
+    }
+
+  old = g_build_filename (g_get_user_data_dir (),
+			  "vinagre",
+			  VINAGRE_BOOKMARKS_FILE_OLD,
+			  NULL);
+  if (!g_file_test (old, G_FILE_TEST_EXISTS))
+    {
+      g_free (old);
+      old = g_build_filename (g_get_home_dir (),
+			      ".gnome2",
+			      VINAGRE_BOOKMARKS_FILE_OLD,
+			      NULL);
+      if (!g_file_test (old, G_FILE_TEST_EXISTS))
+	{
+	  g_free (old);
+	  return;
+	}
+    }
+
+  g_message (_("Migrating the bookmarks file to the new format. This operation is only supposed to run once."));
+
+  kf = g_key_file_new ();
+  if (!g_key_file_load_from_file (kf,
+				  old,
+				  G_KEY_FILE_NONE,
+				  &error))
+    {
+      g_warning (_("Error opening old bookmarks file: %s"), error->message);
+      g_warning (_("Migration cancelled"));
+      g_error_free (error);
+      return;
+    }
+
+  entries = create_list (kf);
+  if (save_to_file (entries, filename))
+    {
+      if (g_unlink (old) != 0)
+	g_warning (_("Could not remove the old bookmarks file"));
+    }
+  else
+    g_warning (_("Migration cancelled"));
+
+  g_slist_foreach (entries, (GFunc) g_object_unref, NULL);
+  g_slist_free (entries);
+  g_key_file_free (kf);
+  g_free (old);
+}
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-migration.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-migration.h	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,37 @@
+/*
+ * vinagre-bookmarks-migration.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is a temporary hack to migrate the bookmarks from an .ini format to
+ * a XML one. The new format is used in Vinagre 2.25.
+ */
+
+#ifndef __VINAGRE_BOOKMARKS_MIGRATION_H__
+#define __VINAGRE_BOOKMARKS_MIGRATION_H__
+
+G_BEGIN_DECLS
+
+void vinagre_bookmarks_migration_migrate (const gchar *filename);
+
+G_END_DECLS
+
+#endif  /* __VINAGRE_BOOKMARKS_MIGRATION_H__ */
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-tree.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-tree.c	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,300 @@
+/*
+ * vinagre-bookmarks-tree.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) Jonh Wendell 2008 <wendell bani com br>
+ * 
+ * vinagre is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * vinagre is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "vinagre-bookmarks-tree.h"
+#include "vinagre-bookmarks-entry.h"
+#include "vinagre-bookmarks.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+struct _VinagreBookmarksTreePrivate
+{
+  GtkWidget *tree;
+};
+
+/* TreeModel */
+enum {
+  IMAGE_COL = 0,
+  NAME_COL,
+  ENTRY_COL,
+  NUM_COLS
+};
+
+G_DEFINE_TYPE (VinagreBookmarksTree, vinagre_bookmarks_tree, GTK_TYPE_VBOX);
+
+static void
+vinagre_bookmarks_tree_row_activated_cb (GtkTreeView          *treeview,
+					 GtkTreePath          *path,
+					 GtkTreeViewColumn    *column,
+					 VinagreBookmarksTree *tree)
+{
+  GtkTreeIter   iter;
+  GtkTreeModel *model;
+  GtkWidget    *toplevel;
+
+  model = gtk_tree_view_get_model (treeview);
+
+  gtk_tree_model_get_iter (model, &iter, path);
+  if (gtk_tree_model_iter_has_child (model, &iter))
+    {
+      if (gtk_tree_view_row_expanded (treeview, path))
+        gtk_tree_view_collapse_row (treeview, path);
+      else
+        gtk_tree_view_expand_row (treeview, path, FALSE);
+      return;
+    }
+  else
+    {
+      toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree));
+      if (GTK_IS_WINDOW (toplevel))
+	gtk_window_activate_default (GTK_WINDOW (toplevel));
+    }
+}
+
+static void
+vinagre_bookmarks_fill_tree (GSList *entries, GtkTreeStore *store, GtkTreeIter *parent, GdkPixbuf *pixbuf)
+{
+  GSList                *l;
+  GtkTreeIter            iter;
+  VinagreBookmarksEntry *entry;
+
+  for (l = entries; l; l = l->next)
+    {
+      entry = VINAGRE_BOOKMARKS_ENTRY (l->data);
+      if (vinagre_bookmarks_entry_get_node (entry) != VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+	continue;
+
+      gtk_tree_store_append (store, &iter, parent);
+      gtk_tree_store_set (store, &iter,
+                          IMAGE_COL, pixbuf,
+                          NAME_COL, vinagre_bookmarks_entry_get_name (entry),
+                          ENTRY_COL, entry,
+		          -1);
+      vinagre_bookmarks_fill_tree (vinagre_bookmarks_entry_get_children (entry),
+				   store,
+				   &iter,
+				   pixbuf);
+    }
+}
+
+static gboolean
+vinagre_bookmarks_tree_update_list (VinagreBookmarksTree *tree)
+{
+  GtkTreeStore     *model;
+  GtkTreeIter       iter;
+  GdkPixbuf        *pixbuf;
+  GtkIconTheme     *icon_theme;
+  GtkTreeSelection *selection;
+  GtkTreePath      *path;
+
+  icon_theme = gtk_icon_theme_get_default ();
+  pixbuf = gtk_icon_theme_load_icon  (icon_theme,
+                                      "folder",
+                                      16,
+                                      0,
+                                      NULL);
+
+  model = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (tree->priv->tree)));
+
+  gtk_tree_store_append (model, &iter, NULL);
+  gtk_tree_store_set (model, &iter,
+                      IMAGE_COL, pixbuf,
+                      NAME_COL, _("Root Folder"),
+		      -1);
+
+  vinagre_bookmarks_fill_tree (vinagre_bookmarks_get_all (vinagre_bookmarks_get_default ()),
+			       model,
+			       NULL,
+			       pixbuf);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->priv->tree));
+  gtk_tree_selection_select_iter (selection, &iter);
+
+  g_object_unref (pixbuf);
+  return FALSE;
+}
+
+static void
+vinagre_bookmarks_tree_init (VinagreBookmarksTree *tree)
+{
+  GtkCellRenderer   *cell;
+  GtkWidget         *scroll;
+  GtkTreeSelection  *selection;
+  GtkTreeStore      *model;
+  GtkTreeViewColumn *main_column;
+
+  tree->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree, VINAGRE_TYPE_BOOKMARKS_TREE, VinagreBookmarksTreePrivate);
+
+  /* Create the scrolled window */
+  scroll = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
+                                       GTK_SHADOW_ETCHED_OUT);
+  gtk_widget_set_size_request (scroll, 200, 180);
+  gtk_box_pack_start (GTK_BOX (tree), scroll, TRUE, TRUE, 0);
+
+  /* Create the model */
+  model = gtk_tree_store_new (NUM_COLS,
+                              GDK_TYPE_PIXBUF,
+                              G_TYPE_STRING,
+                              VINAGRE_TYPE_BOOKMARKS_ENTRY);
+
+  tree->priv->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree->priv->tree), FALSE);
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->priv->tree));
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+  g_object_unref (model);
+
+  main_column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_clickable (main_column, FALSE);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (tree->priv->tree), main_column);
+
+  /* Set up the pixbuf column */
+  cell = gtk_cell_renderer_pixbuf_new ();
+  gtk_tree_view_column_pack_start (main_column, cell, FALSE);
+  gtk_tree_view_column_add_attribute (main_column, cell, "pixbuf", IMAGE_COL);
+
+  /* Set up the name column */
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (main_column, cell, TRUE);
+  gtk_tree_view_column_add_attribute (main_column, cell, "text", NAME_COL);
+
+  g_signal_connect (tree->priv->tree,
+		    "row-activated",
+		    G_CALLBACK (vinagre_bookmarks_tree_row_activated_cb),
+		    tree);
+
+  vinagre_bookmarks_tree_update_list (tree);
+
+  gtk_container_add (GTK_CONTAINER(scroll), tree->priv->tree);
+  gtk_widget_show (tree->priv->tree);
+}
+
+static void
+vinagre_bookmarks_tree_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (vinagre_bookmarks_tree_parent_class)->finalize (object);
+}
+
+static void
+vinagre_bookmarks_tree_class_init (VinagreBookmarksTreeClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = vinagre_bookmarks_tree_finalize;
+  g_type_class_add_private (object_class, sizeof (VinagreBookmarksTreePrivate));
+}
+
+
+GtkWidget *
+vinagre_bookmarks_tree_new (void)
+{
+  return GTK_WIDGET (g_object_new (VINAGRE_TYPE_BOOKMARKS_TREE, NULL));
+}
+
+VinagreBookmarksEntry *
+vinagre_bookmarks_tree_get_selected_entry (VinagreBookmarksTree *tree)
+{
+  GtkTreeSelection      *selection;
+  GtkTreeIter            iter;
+  GtkTreeModel          *model;
+  VinagreBookmarksEntry *entry;
+
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_TREE (tree), NULL);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree->priv->tree));
+  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->priv->tree));
+      gtk_tree_model_get (model, 
+			  &iter,
+			  ENTRY_COL, &entry,
+			  -1);
+      return entry;
+    }
+  else
+    return NULL;
+}
+
+struct _find_entry
+{
+  VinagreBookmarksEntry *entry;
+  gboolean               found;
+  GtkTreePath           *path;
+};
+
+static gboolean
+find_entry (GtkTreeModel *model,
+	    GtkTreePath  *path,
+	    GtkTreeIter  *iter,
+	    gpointer      data)
+{
+  VinagreBookmarksEntry *entry;
+  struct _find_entry    *f = data;
+
+  gtk_tree_model_get (model, 
+		      iter,
+		      ENTRY_COL, &entry,
+			  -1);
+  if (entry == f->entry)
+    {
+      f->found = TRUE;
+      f->path  = gtk_tree_path_copy (path);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+vinagre_bookmarks_tree_select_entry (VinagreBookmarksTree *tree,
+				     VinagreBookmarksEntry *entry)
+{
+  GtkTreeModel          *model;
+  GtkTreeIter            iter;
+  GtkTreePath           *path;
+  gboolean               valid;
+  struct _find_entry     f;
+
+  if (!entry)
+    return FALSE;
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_TREE (tree), FALSE);
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry), FALSE);
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree->priv->tree));
+  f.entry = entry;
+  f.found = FALSE;
+
+  gtk_tree_model_foreach (model, find_entry, &f);
+  if (f.found)
+    {
+      gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree->priv->tree), f.path);
+      gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree->priv->tree), f.path, NULL, FALSE);
+      gtk_tree_path_free (f.path);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-tree.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-tree.h	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,63 @@
+/*
+ * vinagre-bookmarks-tree.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) Jonh Wendell 2008 <wendell bani com br>
+ * 
+ * vinagre is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * vinagre is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _VINAGRE_BOOKMARKS_TREE_H_
+#define _VINAGRE_BOOKMARKS_TREE_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "vinagre-bookmarks-entry.h"
+
+G_BEGIN_DECLS
+
+#define VINAGRE_TYPE_BOOKMARKS_TREE             (vinagre_bookmarks_tree_get_type ())
+#define VINAGRE_BOOKMARKS_TREE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), VINAGRE_TYPE_BOOKMARKS_TREE, VinagreBookmarksTree))
+#define VINAGRE_BOOKMARKS_TREE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), VINAGRE_TYPE_BOOKMARKS_TREE, VinagreBookmarksTreeClass))
+#define VINAGRE_IS_BOOKMARKS_TREE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VINAGRE_TYPE_BOOKMARKS_TREE))
+#define VINAGRE_IS_BOOKMARKS_TREE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_BOOKMARKS_TREE))
+#define VINAGRE_BOOKMARKS_TREE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), VINAGRE_TYPE_BOOKMARKS_TREE, VinagreBookmarksTreeClass))
+
+typedef struct _VinagreBookmarksTreeClass   VinagreBookmarksTreeClass;
+typedef struct _VinagreBookmarksTree        VinagreBookmarksTree;
+typedef struct _VinagreBookmarksTreePrivate VinagreBookmarksTreePrivate;
+
+struct _VinagreBookmarksTreeClass
+{
+  GtkVBoxClass parent_class;
+};
+
+struct _VinagreBookmarksTree
+{
+  GtkVBox parent_instance;
+  VinagreBookmarksTreePrivate *priv;
+};
+
+GType			vinagre_bookmarks_tree_get_type (void) G_GNUC_CONST;
+
+GtkWidget*		vinagre_bookmarks_tree_new (void);
+gboolean		vinagre_bookmarks_tree_select_entry       (VinagreBookmarksTree *tree,
+								   VinagreBookmarksEntry *entry);
+VinagreBookmarksEntry*	vinagre_bookmarks_tree_get_selected_entry (VinagreBookmarksTree *tree);
+
+G_END_DECLS
+
+#endif /* _VINAGRE_BOOKMARKS_TREE_H_ */
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-ui.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-ui.c	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,285 @@
+/*
+ * vinagre-bookmarks-ui.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glade/glade.h>
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "vinagre-bookmarks-ui.h"
+#include "vinagre-utils.h"
+#include "vinagre-bookmarks-tree.h"
+
+static void
+show_dialog_folder (VinagreBookmarks      *book,
+		    GtkWindow             *window,
+		    VinagreBookmarksEntry *entry,
+		    gboolean               is_add)
+{
+  GladeXML    *xml;
+  GtkWidget   *dialog, *box, *tree, *name_entry;
+  const gchar *name;
+
+  xml = glade_xml_new (vinagre_utils_get_glade_filename (),
+		       "bookmarks_add_edit_folder_dialog",
+		       NULL);
+  dialog     = glade_xml_get_widget (xml, "bookmarks_add_edit_folder_dialog");
+  name_entry = glade_xml_get_widget (xml, "edit_bookmark_folder_name_entry");
+  box        = glade_xml_get_widget (xml, "folder_box1");
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), window);
+  gtk_entry_set_text (GTK_ENTRY (name_entry), vinagre_bookmarks_entry_get_name (entry));
+  gtk_editable_set_position (GTK_EDITABLE (name_entry), -1);
+
+  tree = vinagre_bookmarks_tree_new ();
+  vinagre_bookmarks_tree_select_entry  (VINAGRE_BOOKMARKS_TREE (tree),
+					vinagre_bookmarks_entry_get_parent (entry));
+  gtk_box_pack_end (GTK_BOX (box), tree, TRUE, TRUE, 0);
+
+  gtk_widget_show_all (dialog);
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+    {
+      if (is_add)
+        g_object_unref (entry);
+      goto finalize;
+    }
+
+  name = gtk_entry_get_text (GTK_ENTRY (name_entry));
+  if (strlen (name) < 1)
+    {
+      vinagre_utils_show_error (NULL, _("Invalid name for this folder"), window);
+      if (is_add)
+        g_object_unref (entry);
+      goto finalize;
+    }
+
+  vinagre_bookmarks_entry_set_name (entry, name);
+  if (!is_add)
+    {
+      g_object_ref (entry);
+      vinagre_bookmarks_remove_entry (book, entry);
+    }
+  vinagre_bookmarks_add_entry ( book,
+				entry,
+				vinagre_bookmarks_tree_get_selected_entry (VINAGRE_BOOKMARKS_TREE (tree)));
+
+finalize:
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+  g_object_unref (G_OBJECT (xml));
+}
+
+static void
+show_dialog_conn (VinagreBookmarks      *book,
+		  GtkWindow             *window,
+		  VinagreBookmarksEntry *entry,
+		  gboolean               is_add)
+{
+  gchar             *str, *host, *error_str;
+  gint               port;
+  GladeXML          *xml;
+  GtkWidget         *dialog, *host_entry, *name_entry;
+  GtkWidget         *fs_check, *sc_check, *vo_check;
+  GtkWidget         *box, *tree;
+  VinagreConnection *conn;
+  const gchar       *name;
+
+  xml = glade_xml_new (vinagre_utils_get_glade_filename (),
+		       "bookmarks_add_edit_conn_dialog",
+		       NULL);
+  dialog     = glade_xml_get_widget (xml, "bookmarks_add_edit_conn_dialog");
+  name_entry = glade_xml_get_widget (xml, "edit_bookmark_name_entry");
+  host_entry = glade_xml_get_widget (xml, "edit_bookmark_host_entry");
+  fs_check   = glade_xml_get_widget (xml, "edit_fullscreen_check");
+  sc_check   = glade_xml_get_widget (xml, "edit_scaling_check");
+  vo_check   = glade_xml_get_widget (xml, "edit_viewonly_check");
+  box        = glade_xml_get_widget (xml, "folder_box");
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), window);
+  conn = vinagre_bookmarks_entry_get_conn (entry);
+
+  str = vinagre_connection_get_best_name (conn);
+  gtk_entry_set_text (GTK_ENTRY (name_entry), str);
+  gtk_editable_set_position (GTK_EDITABLE (name_entry), -1);
+  g_free (str);
+
+  str = vinagre_connection_get_string_rep (conn, FALSE);
+  gtk_entry_set_text (GTK_ENTRY (host_entry), str);
+  g_free (str);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fs_check),
+				vinagre_connection_get_fullscreen (conn));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc_check),
+				vinagre_connection_get_scaling (conn));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vo_check),
+				vinagre_connection_get_view_only (conn));
+
+  tree = vinagre_bookmarks_tree_new ();
+  vinagre_bookmarks_tree_select_entry  (VINAGRE_BOOKMARKS_TREE (tree),
+					vinagre_bookmarks_entry_get_parent (entry));
+  gtk_box_pack_end (GTK_BOX (box), tree, TRUE, TRUE, 0);
+
+  gtk_widget_show_all (dialog);
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+    goto finalize;
+
+  if (!vinagre_connection_split_string (gtk_entry_get_text (GTK_ENTRY (host_entry)),
+					&host,
+					&port,
+					&error_str))
+    {
+      vinagre_utils_show_error (NULL, error_str, window);
+      g_free (error_str);
+      goto finalize;
+    }
+
+  vinagre_connection_set_host (conn, host);
+  vinagre_connection_set_port (conn, port);
+  vinagre_connection_set_view_only  (conn,
+				     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vo_check)));
+  vinagre_connection_set_scaling    (conn,
+				     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sc_check)));
+  vinagre_connection_set_fullscreen (conn,
+				     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fs_check)));
+
+  name = gtk_entry_get_text (GTK_ENTRY (name_entry));
+  if (strlen (name) < 1)
+    if (strlen (vinagre_connection_get_name (conn)) < 1)
+      name = vinagre_connection_get_host (conn);
+    else
+      name = vinagre_connection_get_name (conn);
+  vinagre_connection_set_name (conn, name);
+
+  if (!is_add)
+    {
+      g_object_ref (entry);
+      vinagre_bookmarks_remove_entry (book, entry);
+    }
+  vinagre_bookmarks_add_entry ( book,
+				entry,
+				vinagre_bookmarks_tree_get_selected_entry (VINAGRE_BOOKMARKS_TREE (tree)));
+
+finalize:
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+  g_object_unref (G_OBJECT (xml));
+}
+
+void
+vinagre_bookmarks_add (VinagreBookmarks  *book,
+                       VinagreConnection *conn,
+		       GtkWindow         *window)
+{
+  VinagreBookmarksEntry *entry;
+
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS (book));
+  g_return_if_fail (VINAGRE_IS_CONNECTION (conn));
+
+  entry = vinagre_bookmarks_entry_new_conn (conn);
+  show_dialog_conn (book, window, entry, TRUE);
+}
+
+void
+vinagre_bookmarks_edit (VinagreBookmarks      *book,
+                        VinagreBookmarksEntry *entry,
+		        GtkWindow             *window)
+{
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS (book));
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+
+  g_object_ref (entry);
+
+  switch (vinagre_bookmarks_entry_get_node (entry))
+    {
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	show_dialog_folder (book, window, entry, FALSE);
+	break;
+
+      case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+        show_dialog_conn (book, window, entry, FALSE);
+        break;
+
+      default:
+	g_assert_not_reached ();
+    }
+
+  g_object_unref (entry);
+}
+
+void
+vinagre_bookmarks_del (VinagreBookmarks      *book,
+                       VinagreBookmarksEntry *entry,
+		       GtkWindow             *window)
+{
+  GtkWidget *dialog;
+  gchar     *name, *title, *msg1, *msg2;
+  GError    *error = NULL;
+  GSList    *parent;
+
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS (book));
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (entry));
+
+  msg1 = g_strdup (_("Are you sure you want to remove <i>%s</i> from bookmarks?"));
+
+  if (vinagre_bookmarks_entry_get_node (entry) == VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+    {
+      name = g_strdup (vinagre_bookmarks_entry_get_name (entry));
+      title = g_strdup (_("Remove Folder?"));
+      msg2 = g_strdup_printf ("%s\n\n%s", msg1, _("Notice that all its subfolders and items will be removed as well."));
+    }
+  else
+    {
+      name = vinagre_connection_get_best_name (vinagre_bookmarks_entry_get_conn (entry));
+      title = g_strdup (_("Remove Item?"));
+      msg2 = g_strdup (msg1);
+    }
+
+  dialog = gtk_message_dialog_new (window,
+				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+				   GTK_MESSAGE_QUESTION,
+				   GTK_BUTTONS_OK_CANCEL,
+				   "%s",
+				   title);
+
+  gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
+					      msg2,
+					      name);
+ 
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+    if (!vinagre_bookmarks_remove_entry (book, entry))
+      g_warning (_("Error removing bookmark: Entry not found"));
+
+  gtk_widget_destroy (dialog);
+  g_free (name);
+  g_free (title);
+  g_free (msg1);
+  g_free (msg2);
+}
+
+void
+vinagre_bookmarks_new_folder (VinagreBookmarks *book,
+			      GtkWindow        *window)
+{
+  VinagreBookmarksEntry *entry;
+
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS (book));
+
+  entry = vinagre_bookmarks_entry_new_folder (_("New Folder"));
+  show_dialog_folder (book, window, entry, TRUE);
+}
+
+/* vim: set ts=8: */

Added: trunk/src/vinagre-bookmarks-ui.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bookmarks-ui.h	Thu Dec 11 17:40:20 2008
@@ -0,0 +1,50 @@
+/*
+ * vinagre-bookmarks-ui.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008  Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_BOOKMARKS_UI_H__
+#define __VINAGRE_BOOKMARKS_UI_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "vinagre-connection.h"
+#include "vinagre-bookmarks.h"
+#include "vinagre-bookmarks-entry.h"
+#include "vinagre-window.h"
+
+G_BEGIN_DECLS
+
+void   vinagre_bookmarks_add        (VinagreBookmarks  *book,
+                                     VinagreConnection *conn,
+                                     GtkWindow         *window);
+void   vinagre_bookmarks_del        (VinagreBookmarks      *book,
+                                     VinagreBookmarksEntry *entry,
+                                     GtkWindow             *window);
+void   vinagre_bookmarks_edit       (VinagreBookmarks      *book,
+                                     VinagreBookmarksEntry *entry,
+                                     GtkWindow             *window);
+void   vinagre_bookmarks_new_folder (VinagreBookmarks      *book,
+                                     GtkWindow             *window);
+
+G_END_DECLS
+
+#endif  /* __VINAGRE_BOOKMARKS_UI_H__ */
+
+/* vim: set ts=8: */

Modified: trunk/src/vinagre-bookmarks.c
==============================================================================
--- trunk/src/vinagre-bookmarks.c	(original)
+++ trunk/src/vinagre-bookmarks.c	Thu Dec 11 17:40:20 2008
@@ -18,20 +18,20 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "vinagre-bookmarks.h"
-#include "vinagre-utils.h"
-
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
-#include <glade/glade.h>
-#include <string.h>
+#include <libxml/parser.h>
+#include <libxml/xmlwriter.h>
+
+#include "vinagre-bookmarks.h"
+#include "vinagre-bookmarks-entry.h"
+#include "vinagre-bookmarks-migration.h"
 
 struct _VinagreBookmarksPrivate
 {
-  GKeyFile     *file;
   gchar        *filename;
-  GSList       *conns;
+  GSList       *entries, *conns;
   GFileMonitor *monitor;
 };
 
@@ -41,24 +41,18 @@
   LAST_SIGNAL
 };
 
-#define VINAGRE_BOOKMARKS_FILE  "vinagre.bookmarks"
-
 G_DEFINE_TYPE (VinagreBookmarks, vinagre_bookmarks, G_TYPE_OBJECT);
 
 static VinagreBookmarks *book_singleton = NULL;
 static guint signals[LAST_SIGNAL] = { 0 };
 
 /* Prototypes */
-static void vinagre_bookmarks_update_file  (VinagreBookmarks *book);
-static void vinagre_bookmarks_update_conns (VinagreBookmarks *book);
-static void vinagre_bookmarks_save_file    (VinagreBookmarks *book);
-static void vinagre_bookmarks_clear_conns  (VinagreBookmarks *book);
-static void vinagre_bookmarks_file_changed (GFileMonitor     *monitor,
-		                            GFile             *file,
-		                            GFile             *other_file,
-		                            GFileMonitorEvent  event_type,
-		                            VinagreBookmarks  *book);
-
+static void vinagre_bookmarks_update_from_file (VinagreBookmarks *book);
+static void vinagre_bookmarks_file_changed     (GFileMonitor     *monitor,
+					        GFile             *file,
+					        GFile             *other_file,
+					        GFileMonitorEvent  event_type,
+					        VinagreBookmarks  *book);
 
 static void
 vinagre_bookmarks_init (VinagreBookmarks *book)
@@ -66,60 +60,37 @@
   GFile *gfile;
 
   book->priv = G_TYPE_INSTANCE_GET_PRIVATE (book, VINAGRE_TYPE_BOOKMARKS, VinagreBookmarksPrivate);
-
-  book->priv->conns = NULL;
-  book->priv->file = NULL;
-
+  book->priv->entries = NULL;
   book->priv->filename = g_build_filename (g_get_user_data_dir (),
 			                   "vinagre",
 			                   VINAGRE_BOOKMARKS_FILE,
 			                   NULL);
-  gfile = g_file_new_for_path (book->priv->filename);
 
   if (!g_file_test (book->priv->filename, G_FILE_TEST_EXISTS))
-    {
-      gchar *old;
-
-      old = g_build_filename (g_get_home_dir (),
-			      ".gnome2",
-			      VINAGRE_BOOKMARKS_FILE,
-			      NULL);
-      if (g_file_test (old, G_FILE_TEST_EXISTS))
-	{
-	  GFile *src, *parent;
-	  GError *error = NULL;
-
-	  g_message (_("Copying the bookmarks file to the new location. This operation is only supposed to run once."));
-	  src = g_file_new_for_path (old);
+    vinagre_bookmarks_migration_migrate (book->priv->filename);
 
-	  parent = g_file_get_parent (gfile);
-	  g_file_make_directory_with_parents (parent, NULL, NULL);
-	  g_object_unref (parent);
-
-	  if (!g_file_copy (src, gfile, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
-	    {
-	      g_warning ("%s", error->message);
-	      g_error_free (error);
-	    }
-
-	  g_object_unref (src);
-	}
-
-      g_free (old);
-    }
-
-  vinagre_bookmarks_update_file (book);
-  vinagre_bookmarks_update_conns (book);
+  vinagre_bookmarks_update_from_file (book);
 
+  gfile = g_file_new_for_path (book->priv->filename);
   book->priv->monitor = g_file_monitor_file (gfile,
                                              G_FILE_MONITOR_NONE,
                                              NULL,
                                              NULL);
+  g_object_unref (gfile);
+
   g_signal_connect (book->priv->monitor,
                     "changed",
                     G_CALLBACK (vinagre_bookmarks_file_changed),
                     book);
-  g_object_unref (gfile);
+}
+
+static void
+vinagre_bookmarks_clear_entries (VinagreBookmarks *book)
+{
+  g_slist_foreach (book->priv->entries, (GFunc) g_object_unref, NULL);
+  g_slist_free (book->priv->entries);
+
+  book->priv->entries = NULL;
 }
 
 static void
@@ -127,28 +98,39 @@
 {
   VinagreBookmarks *book = VINAGRE_BOOKMARKS (object);
 
-  g_key_file_free (book->priv->file);
-  book->priv->file = NULL;
-  vinagre_bookmarks_clear_conns (book);
-
   g_free (book->priv->filename);
   book->priv->filename = NULL;
 
-  g_file_monitor_cancel (book->priv->monitor);
-  g_object_unref (book->priv->monitor);
-
   G_OBJECT_CLASS (vinagre_bookmarks_parent_class)->finalize (object);
 }
 
 static void
+vinagre_bookmarks_dispose (GObject *object)
+{
+  VinagreBookmarks *book = VINAGRE_BOOKMARKS (object);
+
+  if (book->priv->entries)
+    vinagre_bookmarks_clear_entries (book);
+
+  if (book->priv->monitor)
+    {
+      g_file_monitor_cancel (book->priv->monitor);
+      g_object_unref (book->priv->monitor);
+      book->priv->monitor = NULL;
+    }
+
+  G_OBJECT_CLASS (vinagre_bookmarks_parent_class)->dispose (object);
+}
+
+static void
 vinagre_bookmarks_class_init (VinagreBookmarksClass *klass)
 {
   GObjectClass* object_class = G_OBJECT_CLASS (klass);
-  GObjectClass* parent_class = G_OBJECT_CLASS (klass);
 
   g_type_class_add_private (klass, sizeof (VinagreBookmarksPrivate));
 
   object_class->finalize = vinagre_bookmarks_finalize;
+  object_class->dispose  = vinagre_bookmarks_dispose;
 
   signals[BOOKMARK_CHANGED] =
 		g_signal_new ("changed",
@@ -161,324 +143,255 @@
 			      0);
 }
 
-VinagreBookmarks *
-vinagre_bookmarks_get_default (void)
-{
-  if (G_UNLIKELY (!book_singleton))
-    book_singleton = VINAGRE_BOOKMARKS (g_object_new (VINAGRE_TYPE_BOOKMARKS,
-                                                      NULL));
-  return book_singleton;
-}
-
 static VinagreConnection *
-vinagre_bookmarks_find_conn (VinagreBookmarks  *book,
-                             VinagreConnection *conn)
+find_conn_by_host (GSList *entries, const gchar *host, gint port)
 {
-  GSList *l, *next;
+  GSList *l;
+  VinagreConnection *conn;
 
-  for (l = book->priv->conns; l; l = next)
+  for (l = entries; l; l = l->next)
     {
-      VinagreConnection *local = VINAGRE_CONNECTION (l->data);
+      VinagreBookmarksEntry *entry = VINAGRE_BOOKMARKS_ENTRY (l->data);
 
-      if ( (g_str_equal (vinagre_connection_get_host (conn),
-                         vinagre_connection_get_host (local)))
-          &&
-            (vinagre_connection_get_port (conn) == vinagre_connection_get_port (local) ) )
-        return local;
+      switch (vinagre_bookmarks_entry_get_node (entry))
+	{
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	    if ((conn = find_conn_by_host (vinagre_bookmarks_entry_get_children (entry),
+					  host,
+					  port)))
+	      return conn;
+	    break;
+
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	    conn = vinagre_bookmarks_entry_get_conn (entry);
+	    if ( (g_str_equal (host, vinagre_connection_get_host (conn))) &&
+		 (port == vinagre_connection_get_port (conn) ) )
+	      return g_object_ref (conn);
+	    break;
 
-      next = l->next;
+	  default:
+	    g_assert_not_reached ();
+	}
     }
-
   return NULL;
 }
 
-static void
-vinagre_bookmarks_add_conn (VinagreBookmarks  *book,
-                            VinagreConnection *conn)
+VinagreConnection *
+vinagre_bookmarks_exists (VinagreBookmarks *book,
+                          const gchar *host,
+                          gint port)
 {
-  book->priv->conns = g_slist_prepend (book->priv->conns,
-                                       vinagre_connection_clone (conn));
-  vinagre_bookmarks_save_file (book);
-}
+  VinagreConnection *conn = NULL;
+  GSList *l, *next;
 
-static void
-vinagre_bookmarks_edit_conn (VinagreBookmarks  *book,
-                             VinagreConnection *old_conn,
-                             VinagreConnection *conn)
-{
-  VinagreConnection *local = vinagre_bookmarks_find_conn (book, old_conn);
-
-  g_return_if_fail (VINAGRE_IS_CONNECTION (local));
-
-  g_object_unref (local);
-  book->priv->conns = g_slist_remove (book->priv->conns,
-                                      local);
-  book->priv->conns = g_slist_prepend (book->priv->conns,
-                                       vinagre_connection_clone (conn));
-  
-  vinagre_bookmarks_save_file (book);
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), NULL);
+  g_return_val_if_fail (host != NULL, NULL);
+
+  return find_conn_by_host (book->priv->entries, host, port);
 }
 
 static void
-vinagre_bookmarks_del_conn (VinagreBookmarks  *book,
-                            VinagreConnection *conn)
+vinagre_bookmarks_save_fill_xml (GSList *list, xmlTextWriter *writer)
 {
-  VinagreConnection *local = vinagre_bookmarks_find_conn (book, conn);
+  GSList                *l;
+  VinagreBookmarksEntry *entry;
+  VinagreConnection     *conn;
 
-  g_return_if_fail (VINAGRE_IS_CONNECTION (local));
+  for (l = list; l; l = l->next)
+    {
+      entry = (VinagreBookmarksEntry *) l->data;
+      switch (vinagre_bookmarks_entry_get_node (entry))
+	{
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	    xmlTextWriterStartElement (writer, "folder");
+	    xmlTextWriterWriteAttribute (writer, "name", vinagre_bookmarks_entry_get_name (entry));
+
+	    vinagre_bookmarks_save_fill_xml (vinagre_bookmarks_entry_get_children (entry), writer);
+	    xmlTextWriterEndElement (writer);
+	    break;
+
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	    conn = vinagre_bookmarks_entry_get_conn (entry);
+
+	    xmlTextWriterStartElement (writer, "item");
+	    xmlTextWriterWriteElement (writer, "name", vinagre_connection_get_name (conn));
+	    xmlTextWriterWriteElement (writer, "host", vinagre_connection_get_host (conn));
+	    xmlTextWriterWriteFormatElement (writer, "port", "%d", vinagre_connection_get_port (conn));
+	    xmlTextWriterWriteFormatElement (writer, "view_only", "%d", vinagre_connection_get_view_only (conn));
+	    xmlTextWriterWriteFormatElement (writer, "scaling", "%d", vinagre_connection_get_scaling (conn));
+	    xmlTextWriterWriteFormatElement (writer, "fullscreen", "%d", vinagre_connection_get_fullscreen (conn));
 
-  book->priv->conns = g_slist_remove (book->priv->conns,
-                                      local);
-  g_object_unref (local);
-  
-  vinagre_bookmarks_save_file (book);
-}
+	    xmlTextWriterEndElement (writer);
+	    break;
 
-static void
-update_group_from_conn (VinagreBookmarks  *book,
-			VinagreConnection *conn,
-			const gchar *group)
-{
-  g_key_file_set_string (book->priv->file,
-			 group,
-			 "host",
-			 vinagre_connection_get_host (conn));
-  g_key_file_set_integer (book->priv->file,
-			  group,
-			  "port",
-			  vinagre_connection_get_port (conn));
-  g_key_file_set_boolean (book->priv->file,
-			  group,
-			  "view_only",
-			  vinagre_connection_get_view_only (conn));
-  g_key_file_set_boolean (book->priv->file,
-			  group,
-			  "scaling",
-			  vinagre_connection_get_scaling (conn));
-  g_key_file_set_boolean (book->priv->file,
-			  group,
-			  "fullscreen",
-			  vinagre_connection_get_fullscreen (conn));
+	  default:
+	    g_assert_not_reached ();
+	}
+    }
 }
 
-gboolean
-vinagre_bookmarks_add (VinagreBookmarks  *book,
-                       VinagreConnection *conn,
-		       GtkWindow         *window)
-{
-  gint result;
-  GladeXML    *xml;
-  const gchar *glade_file, *name;
-  GtkWidget   *dialog, *name_entry;
-  gchar       *tmp;
-
-  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), FALSE);
-  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), FALSE);
-
-  g_object_ref (conn);
+static gboolean
+vinagre_bookmarks_parse_boolean (const gchar* value)
+{
+  if (g_ascii_strcasecmp (value, "true") == 0 || strcmp (value, "1") == 0)
+    return TRUE;
 
-  glade_file = vinagre_utils_get_glade_filename ();
-  xml = glade_xml_new (glade_file, "add_to_bookmarks_dialog", NULL);
-  dialog = glade_xml_get_widget (xml, "add_to_bookmarks_dialog");
-  name_entry = glade_xml_get_widget (xml, "bookmark_name_entry");
-  gtk_window_set_transient_for (GTK_WINDOW(dialog), window);
+  return FALSE;
+}
 
-  tmp = vinagre_connection_get_best_name(conn);
-  gtk_entry_set_text (GTK_ENTRY (name_entry), tmp);
-  gtk_editable_set_position (GTK_EDITABLE (name_entry), -1);
-  g_free (tmp);
+static VinagreBookmarksEntry *
+vinagre_bookmarks_parse_item (xmlNode *root)
+{
+  VinagreBookmarksEntry *entry = NULL;
+  VinagreConnection     *conn;
+  xmlNode               *curr;
+  xmlChar               *s_value;
 
-  gtk_widget_show_all (dialog);
- 
-  result = gtk_dialog_run (GTK_DIALOG (dialog));
+  conn = vinagre_connection_new ();
 
-  if (result == GTK_RESPONSE_OK)
+  for (curr = root->children; curr; curr = curr->next)
     {
-      name = gtk_entry_get_text (GTK_ENTRY (name_entry));
-      if (strlen(name) < 1)
-	name = vinagre_connection_get_host (conn);
+      s_value = xmlNodeGetContent (curr);
 
-      update_group_from_conn (book, conn, name);
+      if (!xmlStrcmp(curr->name, (const xmlChar *)"host"))
+	vinagre_connection_set_host (conn, s_value);
+      else if (!xmlStrcmp(curr->name, (const xmlChar *)"name"))
+	vinagre_connection_set_name (conn, s_value);
+      else if (!xmlStrcmp(curr->name, (const xmlChar *)"port"))
+	vinagre_connection_set_port (conn, atoi (s_value));
+      else if (!xmlStrcmp(curr->name, (const xmlChar *)"view_only"))
+	vinagre_connection_set_view_only (conn, vinagre_bookmarks_parse_boolean (s_value));
+      else if (!xmlStrcmp(curr->name, (const xmlChar *)"scaling"))
+	vinagre_connection_set_scaling (conn, vinagre_bookmarks_parse_boolean (s_value));
+      else if (!xmlStrcmp(curr->name, (const xmlChar *)"fullscreen"))
+	vinagre_connection_set_fullscreen (conn, vinagre_bookmarks_parse_boolean (s_value));
 
-      vinagre_connection_set_name (conn, name);
-      vinagre_bookmarks_add_conn (book, conn);
+      xmlFree (s_value);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
-  g_object_unref (G_OBJECT (xml));
-  g_object_unref (conn);
-
-  return (result == GTK_RESPONSE_OK);
-}
+  if (vinagre_connection_get_port (conn) <= 0)
+    vinagre_connection_set_port (conn, 5900);
 
-gboolean
-vinagre_bookmarks_edit (VinagreBookmarks  *book,
-                        VinagreConnection *conn,
-		        GtkWindow         *window)
-{
-  gint result;
-  GladeXML    *xml;
-  const gchar *glade_file;
-  GtkWidget   *dialog, *host_entry, *name_entry;
-  GtkWidget   *fs_check, *sc_check, *vo_check;
-  gchar       *str;
+  if (vinagre_connection_get_host (conn))
+    entry = vinagre_bookmarks_entry_new_conn (conn);
 
-  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), FALSE);
-  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), FALSE);
+  g_object_unref (conn);
 
-  glade_file = vinagre_utils_get_glade_filename ();
-  xml = glade_xml_new (glade_file, "edit_bookmark_dialog", NULL);
-  dialog = glade_xml_get_widget (xml, "edit_bookmark_dialog");
-  gtk_window_set_transient_for (GTK_WINDOW(dialog), window);
-
-  name_entry = glade_xml_get_widget (xml, "edit_bookmark_name_entry");
-  host_entry = glade_xml_get_widget (xml, "edit_bookmark_host_entry");
-  fs_check   = glade_xml_get_widget (xml, "edit_fullscreen_check");
-  sc_check   = glade_xml_get_widget (xml, "edit_scaling_check");
-  vo_check   = glade_xml_get_widget (xml, "edit_viewonly_check");
-
-  gtk_entry_set_text (GTK_ENTRY(name_entry), vinagre_connection_get_name (conn));
-  str = vinagre_connection_get_string_rep (conn, FALSE);
-  gtk_entry_set_text (GTK_ENTRY(host_entry), str);
-  g_free (str);
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fs_check),
-				vinagre_connection_get_fullscreen (conn));
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc_check),
-				vinagre_connection_get_scaling (conn));
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vo_check),
-				vinagre_connection_get_view_only (conn));
-
-  gtk_widget_show_all (dialog);
- 
-  result = gtk_dialog_run (GTK_DIALOG (dialog));
-
-  if (result == GTK_RESPONSE_OK)
-    {
-      const gchar *name;
-      gchar *host, *error_str;
-      gint port;
-      VinagreConnection *old_conn = vinagre_connection_clone (conn);
-
-      g_key_file_remove_group (book->priv->file, vinagre_connection_get_name (conn), NULL);
-
-      name = gtk_entry_get_text (GTK_ENTRY (name_entry));
-      if (vinagre_connection_split_string (gtk_entry_get_text (GTK_ENTRY (host_entry)),
-					   &host,
-					   &port,
-					   &error_str))
-	{
-	  vinagre_connection_set_host (conn, host);
-	  vinagre_connection_set_port (conn, port);
-	  vinagre_connection_set_view_only (conn,
-					    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vo_check)));
-	  vinagre_connection_set_scaling (conn,
-					  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sc_check)));
-	  vinagre_connection_set_fullscreen (conn,
-					     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fs_check)));
-
-	  if (strlen (name) < 1)
-	    if (strlen (vinagre_connection_get_name (conn)) < 1)
-	      name = vinagre_connection_get_host (conn);
-	    else
-	      name = vinagre_connection_get_name (conn);
+  return entry;
+}
 
-	  update_group_from_conn (book, conn, name);
+static void
+vinagre_bookmarks_parse_xml (VinagreBookmarks *book, xmlNode *root, VinagreBookmarksEntry *parent_entry)
+{
+  xmlNode *curr;
+  xmlChar *folder_name;
+  VinagreBookmarksEntry *entry;
 
-	  vinagre_connection_set_name (conn, name);
-	  vinagre_bookmarks_edit_conn (book, old_conn, conn);
-	}
-      else
+  for (curr = root; curr; curr = curr->next)
+    {
+      if (curr->type == XML_ELEMENT_NODE)
 	{
-	  vinagre_utils_show_error (NULL, error_str, window);
-	  g_free (error_str);
+	  if (!xmlStrcmp(curr->name, (const xmlChar *)"folder"))
+	    {
+	      folder_name = xmlGetProp (curr, (const xmlChar *)"name");
+	      if (folder_name && *folder_name)
+		{
+		  entry = vinagre_bookmarks_entry_new_folder ((const gchar *) folder_name);
+		  if (parent_entry)
+		    vinagre_bookmarks_entry_add_child (parent_entry, entry);
+		  else
+		    book->priv->entries = g_slist_insert_sorted (book->priv->entries,
+							         entry,
+							        (GCompareFunc)vinagre_bookmarks_entry_compare);
+
+		  vinagre_bookmarks_parse_xml (book, curr->children, entry);
+		}
+	      xmlFree (folder_name);
+	    }
+	  else if (!xmlStrcmp(curr->name, (const xmlChar *)"item"))
+	    {
+	      entry = vinagre_bookmarks_parse_item (curr);
+	      if (entry)
+		{
+		  if (parent_entry)
+		    vinagre_bookmarks_entry_add_child (parent_entry, entry);
+		  else
+		    book->priv->entries = g_slist_insert_sorted (book->priv->entries,
+								 entry,
+								 (GCompareFunc)vinagre_bookmarks_entry_compare);
+		}
+	    }
 	}
-
-      g_object_unref (old_conn);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
-  g_object_unref (G_OBJECT (xml));
-
-  return (result == GTK_RESPONSE_OK);
 }
 
-gboolean
-vinagre_bookmarks_del (VinagreBookmarks  *book,
-                       VinagreConnection *conn,
-		       GtkWindow         *window)
-{
-  gint       result;
-  GtkWidget *dialog;
-  gchar     *name;
-  GError    *error = NULL;
+static void
+vinagre_bookmarks_update_from_file (VinagreBookmarks *book)
+{
+  xmlErrorPtr error;
+  xmlNodePtr  root;
+  xmlDocPtr   doc;
 
-  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), FALSE);
-  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), FALSE);
+  if (!g_file_test (book->priv->filename, G_FILE_TEST_EXISTS))
+    return;
 
-  name = vinagre_connection_get_best_name (conn);
-  if (!g_key_file_has_group (book->priv->file, name))
+  doc = xmlReadFile (book->priv->filename, NULL, XML_PARSE_NOERROR);
+
+  if (!doc)
     {
-      g_free (name);
-      return FALSE;
+      error = xmlGetLastError ();
+      g_warning (_("Error while initializing bookmarks: %s"), error?error->message: _("Unknown error"));
+      return;
     }
 
-  dialog = gtk_message_dialog_new (window,
-				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-				   GTK_MESSAGE_QUESTION,
-				   GTK_BUTTONS_OK_CANCEL,
-				   _("Confirm removal?"));
-
-  gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
-					    _("Are you sure you want to remove <i>%s</i> from bookmarks?"),
-					    name);
- 
-  result = gtk_dialog_run (GTK_DIALOG (dialog));
-  gtk_widget_destroy (dialog);
+  root = xmlDocGetRootElement (doc);
+  if (!root)
+    {
+      g_warning (_("Error while initializing bookmarks: The file seems to be empty"));
+      xmlFreeDoc (doc);
+      return;
+    }
 
-  if (result == GTK_RESPONSE_OK)
+  if (xmlStrcmp (root->name, (const xmlChar *) "vinagre-bookmarks"))
     {
-      g_key_file_remove_group (book->priv->file, name, &error);
-      if (error)
-	{
-	  g_warning (_("Error while removing %s from bookmarks: %s"),
-			name,
-			error->message);
-	  g_error_free (error);
-	  g_free (name);
-	  return FALSE;
-	}
-      vinagre_bookmarks_del_conn (book, conn);
+      g_warning (_("Error while initializing bookmarks: The file is not a vinagre bookmarks file"));
+      xmlFreeDoc (doc);
+      return;
     }
 
-  g_free (name);
-  return (result == GTK_RESPONSE_OK);
+  vinagre_bookmarks_clear_entries (book);
+  vinagre_bookmarks_parse_xml (book, root->xmlChildrenNode, NULL);
+  xmlFreeDoc (doc);
 }
 
-VinagreConnection *
-vinagre_bookmarks_exists (VinagreBookmarks *book,
-                          const gchar *host,
-                          gint port)
+
+static void
+vinagre_bookmarks_file_changed (GFileMonitor      *monitor,
+		                GFile             *file,
+		                GFile             *other_file,
+		                GFileMonitorEvent  event_type,
+		                VinagreBookmarks  *book)
 {
-  VinagreConnection *conn = NULL;
-  GSList *l, *next;
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+      event_type != G_FILE_MONITOR_EVENT_CREATED &&
+      event_type != G_FILE_MONITOR_EVENT_DELETED)
+    return;
 
-  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), NULL);
+  vinagre_bookmarks_update_from_file (book);
 
-  for (l = book->priv->conns; l; l = next)
-    {
-      VinagreConnection *con = VINAGRE_CONNECTION (l->data);
+  g_signal_emit (book, signals[BOOKMARK_CHANGED], 0);
+}
 
-      if ( (g_str_equal (host, vinagre_connection_get_host (con))) &&
-            (port == vinagre_connection_get_port (con) ) )
-        {
-          conn = vinagre_connection_clone (con);
-          break;
-        }
-      next = l->next;
-    }
-  
-  return conn;
+/* Public API */
+
+VinagreBookmarks *
+vinagre_bookmarks_get_default (void)
+{
+  if (G_UNLIKELY (!book_singleton))
+    book_singleton = VINAGRE_BOOKMARKS (g_object_new (VINAGRE_TYPE_BOOKMARKS,
+                                                      NULL));
+  return book_singleton;
 }
 
 GSList *
@@ -486,146 +399,124 @@
 {
   g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), NULL);
 
-  return book->priv->conns;
+  return book->priv->entries;
 }
 
-static void
-vinagre_bookmarks_update_file (VinagreBookmarks *book)
+void
+vinagre_bookmarks_save_to_file (VinagreBookmarks *book)
 {
-  gboolean loaded = TRUE;
-  GError   *error = NULL;
+  xmlTextWriter *writer;
+  xmlBuffer     *buf;
+  int            rc;
+  GError        *error;
+
+  writer = NULL;
+  buf    = NULL;
+  error  = NULL;
 
-  if (book->priv->file)
-    g_key_file_free (book->priv->file);
-  book->priv->file = g_key_file_new ();
-
-  if (g_file_test (book->priv->filename, G_FILE_TEST_EXISTS))
-    loaded = g_key_file_load_from_file (book->priv->file,
-					book->priv->filename,
-					G_KEY_FILE_NONE,
-					&error);
-  if (!loaded)
+  buf = xmlBufferCreate ();
+  if (!buf)
     {
-      if (error)
-	{
-	  g_warning (_("Error while initializing bookmarks: %s"), error->message);
-	  g_error_free (error);
-	}
+      g_warning (_("Error while saving bookmarks: Failed to create the XML structure"));
+      return;
     }
-}
 
-static void
-vinagre_bookmarks_save_file (VinagreBookmarks *book)
-{
-  gchar    *data;
-  gsize    length;
-  GError   *error;
-
-  error = NULL;
-  data = g_key_file_to_data (book->priv->file,
-			     &length,
-			     &error);
-  if (!data)
+  writer = xmlNewTextWriterMemory(buf, 0);
+  if (!writer)
     {
-      if (error)
-	{
-	  g_warning (_("Error while saving bookmarks: %s"), error->message);
-	  g_error_free (error);
-	}
+      g_warning (_("Error while saving bookmarks: Failed to create the XML structure"));
+      goto finalize;
+    }
 
-      return;
+  rc = xmlTextWriterStartDocument (writer, NULL, "utf-8", NULL);
+  if (rc < 0)
+    {
+      g_warning (_("Error while saving bookmarks: Failed to initialize the XML structure"));
+      goto finalize;
+    }
 
+  rc = xmlTextWriterStartElement (writer, "vinagre-bookmarks");
+  if (rc < 0)
+    {
+      g_warning (_("Error while saving bookmarks: Failed to initialize the XML structure"));
+      goto finalize;
     }
 
-  error = NULL;
+  vinagre_bookmarks_save_fill_xml (book->priv->entries, writer);
+
+  rc = xmlTextWriterEndDocument (writer);
+  if (rc < 0)
+    {
+      g_warning (_("Error while saving bookmarks: Failed to finalize the XML structure"));
+      goto finalize;
+    }
 
   if (!g_file_set_contents (book->priv->filename,
-			    data,
-			    length,
+			    (const char *) buf->content,
+			    -1,
 			    &error))
     {
+      g_warning (_("Error while saving bookmarks: %s"), error?error->message:_("Unknown error"));
       if (error)
-	{
-	  g_warning (_("Error while saving bookmarks: %s"), error->message);
-	  g_error_free (error);
-          g_free (data);
-          return;
-	}
+	g_error_free (error);
+      goto finalize;
     }
 
-  g_free (data);
-}
-
-static void
-vinagre_bookmarks_clear_conns (VinagreBookmarks *book)
-{
-  g_slist_foreach (book->priv->conns, (GFunc) g_object_unref, NULL);
-  g_slist_free (book->priv->conns);
-
-  book->priv->conns = NULL;
+finalize:
+  if (writer)
+    xmlFreeTextWriter (writer);
+  if (buf)
+    xmlBufferFree (buf);
+}
+
+void
+vinagre_bookmarks_add_entry (VinagreBookmarks      *book,
+                             VinagreBookmarksEntry *entry,
+                             VinagreBookmarksEntry *parent)
+{
+  /* I do not ref entry */
+  if (parent)
+    vinagre_bookmarks_entry_add_child (parent, entry);
+  else
+    book->priv->entries = g_slist_insert_sorted (book->priv->entries,
+						 entry,
+						 (GCompareFunc)vinagre_bookmarks_entry_compare);
+  vinagre_bookmarks_save_to_file (book);
 }
 
-static void
-vinagre_bookmarks_update_conns (VinagreBookmarks *book)
+gboolean
+vinagre_bookmarks_remove_entry (VinagreBookmarks      *book,
+				VinagreBookmarksEntry *entry)
 {
-  gsize length, i;
-  gchar **conns;
+  GSList *l;
 
-  vinagre_bookmarks_clear_conns (book);
+  g_return_val_if_fail (VINAGRE_IS_BOOKMARKS (book), FALSE);
 
-  conns = g_key_file_get_groups (book->priv->file, &length);
-  for (i=0; i<length; i++)
+  /* I do unref entry */
+  if (g_slist_index (book->priv->entries, entry) > -1)
     {
-      VinagreConnection *conn;
-      gchar             *s_value;
-      gint               i_value;
-      gboolean           b_value;
-
-      s_value = g_key_file_get_string (book->priv->file, conns[i], "host", NULL);
-      if (!s_value)
-        continue;
-
-      conn = vinagre_connection_new ();
-      vinagre_connection_set_name (conn, conns[i]);
-      vinagre_connection_set_host (conn, s_value);
-      g_free (s_value);
-
-      i_value = g_key_file_get_integer (book->priv->file, conns[i], "port", NULL);
-      if (i_value == 0)
-        i_value = 5900;
-      vinagre_connection_set_port (conn, i_value);
-
-      b_value = g_key_file_get_boolean (book->priv->file, conns[i], "view_only", NULL);
-      vinagre_connection_set_view_only (conn, b_value);
-
-      b_value = g_key_file_get_boolean (book->priv->file, conns[i], "fullscreen", NULL);
-      vinagre_connection_set_fullscreen (conn, b_value);
-
-      b_value = g_key_file_get_boolean (book->priv->file, conns[i], "scaling", NULL);
-      vinagre_connection_set_scaling (conn, b_value);
-
-      book->priv->conns = g_slist_prepend (book->priv->conns, conn);
+      book->priv->entries = g_slist_remove (book->priv->entries, entry);
+      g_object_unref (entry);
+      vinagre_bookmarks_save_to_file (book);
+      return TRUE;
     }
 
-  g_strfreev (conns);
-}
+  for (l = book->priv->entries; l; l = l->next)
+    {
+      VinagreBookmarksEntry *e = (VinagreBookmarksEntry *) l->data;
 
-static void
-vinagre_bookmarks_file_changed (GFileMonitor      *monitor,
-		                GFile             *file,
-		                GFile             *other_file,
-		                GFileMonitorEvent  event_type,
-		                VinagreBookmarks  *book)
-{
-  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
-      event_type != G_FILE_MONITOR_EVENT_CREATED &&
-      event_type != G_FILE_MONITOR_EVENT_DELETED)
-    return;
+      if (vinagre_bookmarks_entry_get_node (e) != VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER)
+	continue;
 
-  vinagre_bookmarks_update_file (book);
-  vinagre_bookmarks_update_conns (book);
+      if (vinagre_bookmarks_entry_remove_child (e, entry))
+	{
+	  g_object_unref (entry);
+	  vinagre_bookmarks_save_to_file (book);
+	  return TRUE;
+	}
+    }
 
-  g_signal_emit (book, signals[BOOKMARK_CHANGED], 0);
+  return FALSE;
 }
 
 /* vim: set ts=8: */

Modified: trunk/src/vinagre-bookmarks.h
==============================================================================
--- trunk/src/vinagre-bookmarks.h	(original)
+++ trunk/src/vinagre-bookmarks.h	Thu Dec 11 17:40:20 2008
@@ -22,11 +22,16 @@
 #define __VINAGRE_BOOKMARKS_H__
 
 #include <glib.h>
+#include <glib-object.h>
 
 #include "vinagre-connection.h"
+#include "vinagre-bookmarks-entry.h"
 
 G_BEGIN_DECLS
 
+#define VINAGRE_BOOKMARKS_FILE      "vinagre-bookmarks.xml"
+#define VINAGRE_BOOKMARKS_FILE_OLD  "vinagre.bookmarks"
+
 #define VINAGRE_TYPE_BOOKMARKS             (vinagre_bookmarks_get_type ())
 #define VINAGRE_BOOKMARKS(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), VINAGRE_TYPE_BOOKMARKS, VinagreBookmarks))
 #define VINAGRE_BOOKMARKS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), VINAGRE_TYPE_BOOKMARKS, VinagreBookmarksClass))
@@ -56,19 +61,15 @@
 
 GType vinagre_bookmarks_get_type (void) G_GNUC_CONST;
 
-VinagreBookmarks   *vinagre_bookmarks_get_default (void);
-
-gboolean            vinagre_bookmarks_add   (VinagreBookmarks  *book,
-                                             VinagreConnection *conn,
-                                             GtkWindow         *window);
-gboolean            vinagre_bookmarks_del   (VinagreBookmarks  *book,
-                                             VinagreConnection *conn,
-                                             GtkWindow         *window);
-gboolean            vinagre_bookmarks_edit  (VinagreBookmarks  *book,
-                                             VinagreConnection *conn,
-                                             GtkWindow         *window);
+VinagreBookmarks   *vinagre_bookmarks_get_default  (void);
+GSList             *vinagre_bookmarks_get_all      (VinagreBookmarks *book);
+void                vinagre_bookmarks_save_to_file (VinagreBookmarks *book);
+void                vinagre_bookmarks_add_entry    (VinagreBookmarks      *book,
+                                                    VinagreBookmarksEntry *entry,
+                                                    VinagreBookmarksEntry *parent);
+gboolean           vinagre_bookmarks_remove_entry  (VinagreBookmarks      *book,
+                                                    VinagreBookmarksEntry *entry);
 
-GSList             *vinagre_bookmarks_get_all (VinagreBookmarks *book);
 VinagreConnection  *vinagre_bookmarks_exists  (VinagreBookmarks *book,
                                                const gchar *host,
                                                gint port);

Modified: trunk/src/vinagre-commands.c
==============================================================================
--- trunk/src/vinagre-commands.c	(original)
+++ trunk/src/vinagre-commands.c	Thu Dec 11 17:40:20 2008
@@ -34,6 +34,7 @@
 #include "vinagre-tab.h"
 #include "vinagre-connect.h"
 #include "vinagre-bookmarks.h"
+#include "vinagre-bookmarks-ui.h"
 #include "vinagre-fav.h"
 #include "vinagre-window-private.h"
 #include "vinagre-prefs.h"
@@ -353,14 +354,24 @@
 }
 
 void
+vinagre_cmd_bookmarks_new_folder (GtkAction     *action,
+				  VinagreWindow *window)
+{
+  g_return_if_fail (VINAGRE_IS_WINDOW (window));
+
+  vinagre_bookmarks_new_folder (vinagre_bookmarks_get_default (),
+				GTK_WINDOW (window));
+}
+
+void
 vinagre_cmd_bookmarks_edit (GtkAction     *action,
 			    VinagreWindow *window)
 {
   g_return_if_fail (VINAGRE_IS_WINDOW (window));
-  g_return_if_fail (VINAGRE_IS_CONNECTION (window->priv->fav_conn_selected));
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (window->priv->fav_entry_selected));
 
   vinagre_bookmarks_edit (vinagre_bookmarks_get_default (),
-                          window->priv->fav_conn_selected,
+                          window->priv->fav_entry_selected,
                           GTK_WINDOW (window));
 }
 
@@ -369,10 +380,10 @@
 			   VinagreWindow *window)
 {
   g_return_if_fail (VINAGRE_IS_WINDOW (window));
-  g_return_if_fail (VINAGRE_IS_CONNECTION (window->priv->fav_conn_selected));
+  g_return_if_fail (VINAGRE_IS_BOOKMARKS_ENTRY (window->priv->fav_entry_selected));
 
   vinagre_bookmarks_del (vinagre_bookmarks_get_default (),
-                         window->priv->fav_conn_selected,
+                         window->priv->fav_entry_selected,
                          GTK_WINDOW (window));
 }
 
@@ -386,7 +397,7 @@
 
   conn = g_object_get_data (G_OBJECT (action), "conn");
   if (!conn)
-    conn = window->priv->fav_conn_selected;
+    conn = vinagre_bookmarks_entry_get_conn (window->priv->fav_entry_selected);
 
   g_return_if_fail (VINAGRE_IS_CONNECTION (conn));
 

Modified: trunk/src/vinagre-commands.h
==============================================================================
--- trunk/src/vinagre-commands.h	(original)
+++ trunk/src/vinagre-commands.h	Thu Dec 11 17:40:20 2008
@@ -70,6 +70,8 @@
 						 VinagreWindow *window);
 void		vinagre_cmd_bookmarks_open	(GtkAction     *action,
 						 VinagreWindow *window);
+void		vinagre_cmd_bookmarks_new_folder(GtkAction     *action,
+						 VinagreWindow *window);
 void		vinagre_cmd_bookmarks_edit	(GtkAction     *action,
 						 VinagreWindow *window);
 void		vinagre_cmd_bookmarks_del	(GtkAction     *action,

Modified: trunk/src/vinagre-fav.c
==============================================================================
--- trunk/src/vinagre-fav.c	(original)
+++ trunk/src/vinagre-fav.c	Thu Dec 11 17:40:20 2008
@@ -27,7 +27,7 @@
 #include "vinagre-utils.h"
 #include "vinagre-bookmarks.h"
 #include "vinagre-window-private.h"
-#include "gossip-cell-renderer-expander.h"
+#include "vinagre-bookmarks-entry.h"
 
 #ifdef VINAGRE_ENABLE_AVAHI
 #include "vinagre-mdns.h"
@@ -37,10 +37,9 @@
 
 struct _VinagreFavPrivate
 {
-  VinagreWindow *window;
-  GtkWidget     *tree;
-  GtkTreeModel  *model;
-  GtkTreeViewColumn *main_column;
+  VinagreWindow     *window;
+  GtkWidget         *tree;
+  GtkTreeModel      *model;
 };
 
 G_DEFINE_TYPE(VinagreFav, vinagre_fav, GTK_TYPE_VBOX)
@@ -57,7 +56,7 @@
 enum {
   IMAGE_COL = 0,
   NAME_COL,
-  CONN_COL,
+  ENTRY_COL,
   IS_FOLDER_COL,
   IS_GROUP_COL,
   IS_AVAHI_COL,
@@ -144,7 +143,7 @@
 			      G_SIGNAL_RUN_FIRST,
 			      G_STRUCT_OFFSET (VinagreFavClass, fav_activated),
 			      NULL, NULL,
-			      g_cclosure_marshal_VOID__POINTER,
+			      g_cclosure_marshal_VOID__OBJECT,
 			      G_TYPE_NONE,
 			      1,
 			      G_TYPE_POINTER);
@@ -155,7 +154,7 @@
 			      G_SIGNAL_RUN_FIRST,
 			      G_STRUCT_OFFSET (VinagreFavClass, fav_selected),
 			      NULL, NULL,
-			      g_cclosure_marshal_VOID__POINTER,
+			      g_cclosure_marshal_VOID__OBJECT,
 			      G_TYPE_NONE,
 			      1,
 			      G_TYPE_POINTER);
@@ -170,13 +169,13 @@
 			      VinagreFav         *fav)
 {
   GtkTreeIter iter;
-  VinagreConnection *conn = NULL;
+  VinagreBookmarksEntry *entry;
   gboolean folder, group;
 
   gtk_tree_model_get_iter (fav->priv->model, &iter, path);
   gtk_tree_model_get (fav->priv->model, 
 		      &iter,
-		      CONN_COL, &conn,
+		      ENTRY_COL, &entry,
                       IS_FOLDER_COL, &folder,
                       IS_GROUP_COL, &group,
 		      -1);
@@ -194,7 +193,7 @@
   g_signal_emit (G_OBJECT (fav), 
 		 signals[FAV_ACTIVATED],
 		 0, 
-		 conn);
+		 entry);
 }
 
 static void
@@ -202,26 +201,26 @@
 				  VinagreFav       *fav)
 {
   GtkTreeIter iter;
-  VinagreConnection *conn = NULL;
+  VinagreBookmarksEntry *entry;
   gboolean avahi;
 
   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
     {
       gtk_tree_model_get (fav->priv->model, 
 			  &iter,
-			  CONN_COL, &conn,
+			  ENTRY_COL, &entry,
                           IS_AVAHI_COL, &avahi,
 			  -1);
     }
 
   if (avahi)
-    conn = NULL;
+    entry = NULL;
 
   /* Emits the signal saying that user has selected a bookmark */
   g_signal_emit (G_OBJECT (fav), 
 		 signals[FAV_SELECTED],
 		 0, 
-		 conn);
+		 entry);
 }
 
 static GtkTreePath *
@@ -284,13 +283,14 @@
 }
 
 static gboolean
-show_popup_menu (VinagreFav     *fav,
-		 GdkEventButton *event)
+show_popup_menu (VinagreFav      *fav,
+		 GdkEventButton  *event,
+		 const gchar     *menu_name)
 {
   GtkWidget *menu = NULL;
 
   menu = gtk_ui_manager_get_widget (vinagre_window_get_ui_manager (fav->priv->window),
-				    "/FavPopup");
+				    menu_name);
   g_return_val_if_fail (menu != NULL, FALSE);
 
   if (event)
@@ -313,7 +313,7 @@
 		      0, 
 		      gtk_get_current_event_time ());
 
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+      //gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
 
     }
 
@@ -326,45 +326,46 @@
 			VinagreFav     *fav)
 {
   GtkTreePath *path = NULL;
-  GtkTreeIter iter;
-  gboolean folder, group, avahi;
+  GtkTreeIter  iter;
+  gboolean     folder, group, avahi;
 
   if (!((GDK_BUTTON_PRESS == event->type) && (3 == event->button)))
     return FALSE;
 
-  if (event->window == gtk_tree_view_get_bin_window (treeview))
-    {
-    /* Change the cursor position */
-    if (gtk_tree_view_get_path_at_pos (treeview,
-				       event->x,
-				       event->y,
-				       &path,
-				       NULL,
-				       NULL,
-			               NULL))
-      {				
-        gtk_tree_model_get_iter (fav->priv->model, &iter, path);
-        gtk_tree_model_get (fav->priv->model, 
-		            &iter,
-                            IS_FOLDER_COL, &folder,
-                            IS_GROUP_COL, &group,
-                            IS_AVAHI_COL, &avahi,
-		            -1);
-        if (folder || group || avahi)
-          {
-            gtk_tree_path_free (path);
-            return FALSE;
-          }
-
-        gtk_tree_view_set_cursor (treeview,
-				  path,
-				  NULL,
-				  FALSE);
-	 gtk_tree_path_free (path);
-	 /* A row exists at mouse position */
-	 return show_popup_menu (fav, event);
-      }
+  if (event->window != gtk_tree_view_get_bin_window (treeview))
+    return FALSE;
+
+  if (gtk_tree_view_get_path_at_pos (treeview,
+				     event->x,
+				     event->y,
+				     &path,
+				     NULL,
+				     NULL,
+			             NULL))
+    {				
+      gtk_tree_model_get_iter (fav->priv->model, &iter, path);
+      gtk_tree_model_get (fav->priv->model, 
+			  &iter,
+			  IS_FOLDER_COL, &folder,
+			  IS_GROUP_COL, &group,
+			  IS_AVAHI_COL, &avahi,
+			  -1);
+      if (group || avahi)
+	{
+	  gtk_tree_path_free (path);
+	  return FALSE;
+	}
+
+      gtk_tree_view_set_cursor (treeview,
+				path,
+				NULL,
+				FALSE);
+      gtk_tree_path_free (path);
+      return show_popup_menu (fav, event, folder?"/FavPopupFolder":"/FavPopupConn");
     }
+  else
+    return show_popup_menu (fav, event, "/FavPopupEmpty");
+
   return FALSE;
 }
 
@@ -375,23 +376,26 @@
 {
   /* Only respond if the treeview is the actual focus */
   if (gtk_window_get_focus (GTK_WINDOW (fav->priv->window)) == treeview)
-    return show_popup_menu (fav, NULL);
+    return show_popup_menu (fav, NULL, FALSE);
 
   return FALSE;
 }
 
 static gboolean
 vinagre_fav_tooltip (GtkWidget *widget,
-                     gint x, gint y, gboolean k,
+                     gint       x,
+                     gint       y,
+                     gboolean   k,
                      GtkTooltip *tooltip,
                      VinagreFav *fav)
 {
-  gchar *tip, *name, *uri;
-  GtkTreePath *path = NULL;
-  gint bx, by;
-  GtkTreeIter iter;
-  gboolean folder, group;
-  VinagreConnection *conn = NULL;
+  gchar                 *tip, *name, *uri;
+  GtkTreePath           *path;
+  gint                   bx, by;
+  GtkTreeIter            iter;
+  gboolean               folder, group;
+  VinagreConnection     *conn;
+  VinagreBookmarksEntry *entry;
 
   gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget),
                                                      x, y,
@@ -409,7 +413,7 @@
   gtk_tree_model_get_iter (fav->priv->model, &iter, path);
   gtk_tree_model_get (fav->priv->model, 
 		      &iter,
-		      CONN_COL, &conn, 
+		      ENTRY_COL, &entry, 
                       IS_FOLDER_COL, &folder,
                       IS_GROUP_COL, &group,
 		      -1);
@@ -419,6 +423,7 @@
   if (folder || group)
     return FALSE;
 
+  conn = vinagre_bookmarks_entry_get_conn (entry);
   name = vinagre_connection_get_best_name (conn);
   uri = vinagre_connection_get_string_rep (conn, TRUE);
   tip = g_markup_printf_escaped ("<b>%s</b>\n"
@@ -435,25 +440,6 @@
 }
 
 static void
-vinagre_fav_indent_level1_cell_data_func (GtkTreeViewColumn *tree_column,
-					  GtkCellRenderer   *cell,
-					  GtkTreeModel      *model,
-					  GtkTreeIter       *iter,
-					  VinagreFav        *fav)
-{
-  GtkTreePath *path;
-  int          depth;
-
-  path = gtk_tree_model_get_path (model, iter);
-  depth = gtk_tree_path_get_depth (path);
-  gtk_tree_path_free (path);
-  g_object_set (cell,
-	        "text", " ",
-		"visible", depth > 1,
-		NULL);
-}
-
-static void
 vinagre_fav_cell_set_background (VinagreFav       *fav,
 				 GtkCellRenderer  *cell,
 				 gboolean         is_group,
@@ -563,46 +549,12 @@
 }
 
 static void
-vinagre_fav_expander_cell_data_func (GtkTreeViewColumn *column,
-				     GtkCellRenderer   *cell,
-				     GtkTreeModel      *model,
-				     GtkTreeIter       *iter,
-				     VinagreFav        *fav)
-{
-  gboolean is_group;
-  gboolean is_active;
-
-  gtk_tree_model_get (model, iter,
-		      IS_GROUP_COL, &is_group,
-		      -1);
-
-  if (gtk_tree_model_iter_has_child (model, iter))
-    {
-      GtkTreePath *path;
-      gboolean     row_expanded;
-
-      path = gtk_tree_model_get_path (model, iter);
-      row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path);
-      gtk_tree_path_free (path);
-
-      g_object_set (cell,
-		    "visible", TRUE,
-		    "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
-		    NULL);
-    }
-  else
-    g_object_set (cell, "visible", FALSE, NULL);
-
-  is_active = FALSE;
-  vinagre_fav_cell_set_background (fav, cell, is_group, is_active);
-}
-
-static void
 vinagre_fav_create_tree (VinagreFav *fav)
 {
   GtkCellRenderer   *cell;
   GtkWidget         *scroll;
   GtkTreeSelection  *selection;
+  GtkTreeViewColumn *main_column;
 
   /* Create the scrolled window */
   scroll = gtk_scrolled_window_new (NULL, NULL);
@@ -619,71 +571,42 @@
   fav->priv->model = GTK_TREE_MODEL (gtk_tree_store_new (NUM_COLS,
 							 GDK_TYPE_PIXBUF,
 							 G_TYPE_STRING,
-							 VINAGRE_TYPE_CONNECTION,
+							 VINAGRE_TYPE_BOOKMARKS_ENTRY,
                                                          G_TYPE_BOOLEAN,
                                                          G_TYPE_BOOLEAN,
                                                          G_TYPE_BOOLEAN));
 
   fav->priv->tree = gtk_tree_view_new_with_model (fav->priv->model);
-  g_object_set (fav->priv->tree, "show-expanders", FALSE, NULL);
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (fav->priv->tree), FALSE);
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (fav->priv->tree));
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
   g_signal_connect (selection, "changed", G_CALLBACK (vinagre_fav_selection_changed_cb), fav);
 
-  fav->priv->main_column = gtk_tree_view_column_new ();
-//  gtk_tree_view_column_set_title (fav->priv->main_column, _("S_ource"));
-  gtk_tree_view_column_set_clickable (fav->priv->main_column, FALSE);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (fav->priv->tree), fav->priv->main_column);
-
-  /* Set up the indent level1 column */
-  cell = gtk_cell_renderer_text_new ();
-  gtk_tree_view_column_pack_start (fav->priv->main_column, cell, FALSE);
-  gtk_tree_view_column_set_cell_data_func (fav->priv->main_column,
-					   cell,
-				           (GtkTreeCellDataFunc) vinagre_fav_indent_level1_cell_data_func,
-				           fav,
-					   NULL);
-  g_object_set (cell,
-		"xpad", 0,
-		"visible", FALSE,
-		NULL);
+  main_column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_clickable (main_column, FALSE);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (fav->priv->tree), main_column);
 
   /* Set up the pixbuf column */
   cell = gtk_cell_renderer_pixbuf_new ();
-  gtk_tree_view_column_pack_start (fav->priv->main_column, cell, FALSE);
-  gtk_tree_view_column_set_cell_data_func (fav->priv->main_column,
+  gtk_tree_view_column_pack_start (main_column, cell, FALSE);
+  gtk_tree_view_column_set_cell_data_func (main_column,
 					   cell,
 					   (GtkTreeCellDataFunc) vinagre_fav_pixbuf_cell_data_func,
 					    fav,
 					    NULL);
-  g_object_set (cell,
-		"xpad", 8,
-		"ypad", 1,
-		"visible", FALSE,
-		NULL);
 
   /* Set up the name column */
   cell = gtk_cell_renderer_text_new ();
   g_object_set (cell,
 		"ellipsize", PANGO_ELLIPSIZE_END,
 		 NULL);
-  gtk_tree_view_column_pack_start (fav->priv->main_column, cell, TRUE);
-  gtk_tree_view_column_set_cell_data_func (fav->priv->main_column,
+  gtk_tree_view_column_pack_start (main_column, cell, TRUE);
+  gtk_tree_view_column_set_cell_data_func (main_column,
 				           cell,
 					   (GtkTreeCellDataFunc) vinagre_fav_title_cell_data_func,
 					    fav,
 					    NULL);
 
-  /* Expander */
-  cell = gossip_cell_renderer_expander_new ();
-  gtk_tree_view_column_pack_end (fav->priv->main_column, cell, FALSE);
-  gtk_tree_view_column_set_cell_data_func (fav->priv->main_column,
-					   cell,
-				           (GtkTreeCellDataFunc) vinagre_fav_expander_cell_data_func,
-                                           fav,
-					   NULL);
-
   g_signal_connect (fav->priv->tree,
 		    "row-activated",
 		    G_CALLBACK (vinagre_fav_row_activated_cb),
@@ -850,6 +773,68 @@
 				   NULL));
 }
 
+static void
+vinagre_fav_fill_bookmarks (GtkTreeStore *store, GSList *list, GtkTreeIter *parent_iter, gboolean is_avahi)
+{
+  GdkPixbuf             *pixbuf;
+  GtkIconTheme          *icon_theme;
+  gchar                 *name;
+  VinagreBookmarksEntry *entry;
+  GSList                *l;
+  GtkTreeIter            iter;
+
+ for (l = list; l; l = l->next)
+    {
+      entry = (VinagreBookmarksEntry *) l->data;
+      switch (vinagre_bookmarks_entry_get_node (entry))
+	{
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	    icon_theme = gtk_icon_theme_get_default ();
+	    pixbuf = gtk_icon_theme_load_icon  (icon_theme,
+					        "folder",
+						16,
+						0,
+						NULL);
+
+	    gtk_tree_store_append (store, &iter, parent_iter);
+	    gtk_tree_store_set (store, &iter,
+				IMAGE_COL, pixbuf,
+				NAME_COL, vinagre_bookmarks_entry_get_name (entry),
+				ENTRY_COL, entry,
+				IS_FOLDER_COL, TRUE,
+				IS_GROUP_COL, FALSE,
+				IS_AVAHI_COL, FALSE,
+				-1);
+	    if (pixbuf != NULL)
+	      g_object_unref (pixbuf);
+
+	    vinagre_fav_fill_bookmarks (store, vinagre_bookmarks_entry_get_children (entry), &iter, is_avahi);
+	    break;
+
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	    name = vinagre_connection_get_best_name (vinagre_bookmarks_entry_get_conn (entry));
+	    pixbuf = vinagre_connection_get_icon (vinagre_bookmarks_entry_get_conn (entry));
+
+	    gtk_tree_store_append (store, &iter, parent_iter);
+	    gtk_tree_store_set (store, &iter,
+				IMAGE_COL, pixbuf,
+				NAME_COL, name,
+				ENTRY_COL, entry,
+				IS_FOLDER_COL, FALSE,
+				IS_GROUP_COL, FALSE,
+				IS_AVAHI_COL, is_avahi,
+				-1);
+	    g_free (name);
+	    if (pixbuf != NULL)
+	      g_object_unref (pixbuf);
+	    break;
+
+	  default:
+	    g_assert_not_reached ();
+	}
+    }
+}
+
 gboolean
 vinagre_fav_update_list (VinagreFav *fav)
 {
@@ -857,7 +842,6 @@
   GtkTreeStore      *store;
   GSList            *list, *l, *next;
   GdkPixbuf         *pixbuf;
-  GtkTreePath       *path;
     
   g_return_val_if_fail (VINAGRE_IS_FAV (fav), FALSE);
 
@@ -875,37 +859,7 @@
                       IS_AVAHI_COL, FALSE,
                       -1);
 
-  for (l = list; l; l = next)
-    {
-      gchar *name = NULL;
-      VinagreConnection *conn;
-
-      next = l->next;
-
-      conn = (VinagreConnection *) l->data;
-      g_assert (VINAGRE_IS_CONNECTION (conn));
-
-      name = vinagre_connection_get_best_name (conn);
-      pixbuf = vinagre_connection_get_icon (conn);
-
-      gtk_tree_store_append (store, &iter, &parent_iter);
-      gtk_tree_store_set (store, &iter,
-                          IMAGE_COL, pixbuf,
-                          NAME_COL, name,
-                          CONN_COL, conn,
-                          IS_FOLDER_COL, FALSE,
-                          IS_GROUP_COL, FALSE,
-                          IS_AVAHI_COL, FALSE,
-                          -1);
-      if (name)
-        g_free (name);
-      if (pixbuf != NULL)
-	g_object_unref (pixbuf);
-    }
-
-  path = gtk_tree_path_new_from_string ("0");
-  gtk_tree_view_expand_row (GTK_TREE_VIEW (fav->priv->tree), path, FALSE);
-  gtk_tree_path_free (path);
+  vinagre_fav_fill_bookmarks (store, list, NULL, FALSE);
 
 #ifdef VINAGRE_ENABLE_AVAHI
   list = vinagre_mdns_get_all (vinagre_mdns_get_default ());
@@ -920,37 +874,7 @@
                       IS_AVAHI_COL, FALSE,
                       -1);
 
-  for (l = list; l; l = next)
-    {
-      gchar *name = NULL;
-      VinagreConnection *conn;
-
-      next = l->next;
-
-      conn = (VinagreConnection *) l->data;
-      g_assert (VINAGRE_IS_CONNECTION (conn));
-
-      name = vinagre_connection_get_best_name (conn);
-      pixbuf = vinagre_connection_get_icon (conn);
-
-      gtk_tree_store_append (store, &iter, &parent_iter);
-      gtk_tree_store_set (store, &iter,
-                          IMAGE_COL, pixbuf,
-                          NAME_COL, name,
-                          CONN_COL, conn,
-                          IS_FOLDER_COL, FALSE,
-                          IS_GROUP_COL, FALSE,
-                          IS_AVAHI_COL, TRUE,
-                          -1);
-      if (name)
-        g_free (name);
-      if (pixbuf != NULL)
-	g_object_unref (pixbuf);
-    }
-
-  path = gtk_tree_path_new_from_string ("1");
-  gtk_tree_view_expand_row (GTK_TREE_VIEW (fav->priv->tree), path, FALSE);
-  gtk_tree_path_free (path);
+  vinagre_fav_fill_bookmarks (store, list, NULL, TRUE);
 #endif
 
   return FALSE;

Modified: trunk/src/vinagre-fav.h
==============================================================================
--- trunk/src/vinagre-fav.h	(original)
+++ trunk/src/vinagre-fav.h	Thu Dec 11 17:40:20 2008
@@ -22,7 +22,7 @@
 #define __VINAGRE_FAV_H__
 
 #include <gtk/gtk.h>
-#include "vinagre-connection.h"
+#include "vinagre-bookmarks-entry.h"
 #include "vinagre-window.h"
 
 G_BEGIN_DECLS
@@ -51,10 +51,10 @@
 
   /* Signals */
   void	(* fav_activated)   (VinagreFav *fav,
-			     VinagreConnection *conn);
+			     VinagreBookmarksEntry *entry);
 
   void	(* fav_selected)    (VinagreFav *fav,
-			    VinagreConnection *conn);
+			    VinagreBookmarksEntry *entry);
 };
 
 GType 	    vinagre_fav_get_type    (void) G_GNUC_CONST;

Modified: trunk/src/vinagre-mdns.c
==============================================================================
--- trunk/src/vinagre-mdns.c	(original)
+++ trunk/src/vinagre-mdns.c	Thu Dec 11 17:40:20 2008
@@ -20,13 +20,14 @@
 
 #include "vinagre-mdns.h"
 #include "vinagre-connection.h"
+#include "vinagre-bookmarks-entry.h"
 #include <avahi-gobject/ga-service-browser.h>
 #include <avahi-gobject/ga-service-resolver.h>
 #include <glib/gi18n.h>
 
 struct _VinagreMdnsPrivate
 {
-  GSList           *conns;
+  GSList           *entries;
   GaServiceBrowser *browser;
   GaClient         *client;
 };
@@ -56,7 +57,8 @@
                      GaLookupResultFlags flags,
                      VinagreMdns         *mdns)
 {
-  VinagreConnection *conn;
+  VinagreConnection     *conn;
+  VinagreBookmarksEntry *entry;
 
   conn = vinagre_connection_new ();
   g_object_set (conn,
@@ -64,11 +66,14 @@
                 "port", port,
                 "host", host_name,
                 NULL);
+  entry = vinagre_bookmarks_entry_new_conn (conn);
+  g_object_unref (conn);
 
-  mdns->priv->conns = g_slist_prepend (mdns->priv->conns, conn);
+  mdns->priv->entries = g_slist_insert_sorted (mdns->priv->entries,
+					       entry,
+					       (GCompareFunc)vinagre_bookmarks_entry_compare);
 
   g_object_unref (resolver);
-
   g_signal_emit (mdns, signals[MDNS_CHANGED], 0);
 }
 
@@ -132,16 +137,16 @@
 {
   GSList *l;
 
-  for (l = mdns->priv->conns; l; l = l->next)
+  for (l = mdns->priv->entries; l; l = l->next)
     {
-      VinagreConnection *conn = VINAGRE_CONNECTION (l->data);
-      if (strcmp (vinagre_connection_get_name (conn), name) == 0)
-        {
-          g_object_unref (conn);
-          mdns->priv->conns = g_slist_remove (mdns->priv->conns, conn);
-          g_signal_emit (mdns, signals[MDNS_CHANGED], 0);
-          return;
-        }
+      VinagreBookmarksEntry *entry = VINAGRE_BOOKMARKS_ENTRY (l->data);
+      if (strcmp (vinagre_connection_get_name (vinagre_bookmarks_entry_get_conn (entry)), name) == 0)
+	{
+	  mdns->priv->entries = g_slist_remove (mdns->priv->entries, entry);
+	  g_object_unref (entry);
+	  g_signal_emit (mdns, signals[MDNS_CHANGED], 0);
+	  return;
+	}
     }
 }
 
@@ -152,7 +157,7 @@
 
   mdns->priv = G_TYPE_INSTANCE_GET_PRIVATE (mdns, VINAGRE_TYPE_MDNS, VinagreMdnsPrivate);
 
-  mdns->priv->conns = NULL;
+  mdns->priv->entries = NULL;
   mdns->priv->browser = ga_service_browser_new ("_rfb._tcp");
   mdns->priv->client = ga_client_new (GA_CLIENT_FLAG_NO_FLAGS);
 
@@ -182,12 +187,12 @@
 }
 
 static void
-vinagre_mdns_clear_conns (VinagreMdns *mdns)
+vinagre_mdns_clear_entries (VinagreMdns *mdns)
 {
-  g_slist_foreach (mdns->priv->conns, (GFunc) g_object_unref, NULL);
-  g_slist_free (mdns->priv->conns);
+  g_slist_foreach (mdns->priv->entries, (GFunc) g_object_unref, NULL);
+  g_slist_free (mdns->priv->entries);
 
-  mdns->priv->conns = NULL;
+  mdns->priv->entries = NULL;
 }
 
 static void
@@ -198,7 +203,7 @@
   g_object_unref (mdns->priv->browser);
   g_object_unref (mdns->priv->client);
 
-  vinagre_mdns_clear_conns (mdns);
+  vinagre_mdns_clear_entries (mdns);
 
   G_OBJECT_CLASS (vinagre_mdns_parent_class)->finalize (object);
 }
@@ -239,5 +244,5 @@
 {
   g_return_val_if_fail (VINAGRE_IS_MDNS (mdns), NULL);
 
-  return mdns->priv->conns;
+  return mdns->priv->entries;
 }

Modified: trunk/src/vinagre-ui.h
==============================================================================
--- trunk/src/vinagre-ui.h	(original)
+++ trunk/src/vinagre-ui.h	Thu Dec 11 17:40:20 2008
@@ -55,6 +55,8 @@
     N_("Edit the details of selected bookmark"), G_CALLBACK (vinagre_cmd_bookmarks_edit) },
   { "BookmarksDel", GTK_STOCK_DELETE, N_("_Remove from bookmarks"), NULL,
     N_("Remove current selected connection from bookmarks"), G_CALLBACK (vinagre_cmd_bookmarks_del) },
+  { "BookmarksNewFolder", "folder-new", N_("_New Folder"), NULL,
+    N_("Create a new folder"), G_CALLBACK (vinagre_cmd_bookmarks_new_folder) },
 
   /* Help menu */
   {"HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",

Modified: trunk/src/vinagre-window-private.h
==============================================================================
--- trunk/src/vinagre-window-private.h	(original)
+++ trunk/src/vinagre-window-private.h	Thu Dec 11 17:40:20 2008
@@ -22,7 +22,7 @@
 #define __VINAGRE_WINDOW_PRIVATE_H__
 
 #include "vinagre-window.h"
-#include "vinagre-connection.h"
+#include "vinagre-bookmarks-entry.h"
 
 G_BEGIN_DECLS
 
@@ -54,7 +54,7 @@
   GtkWidget       *menubar;
 
   GtkWidget       *active_tab;
-  VinagreConnection *fav_conn_selected;
+  VinagreBookmarksEntry *fav_entry_selected;
 	
   gint            width;
   gint            height;

Modified: trunk/src/vinagre-window.c
==============================================================================
--- trunk/src/vinagre-window.c	(original)
+++ trunk/src/vinagre-window.c	Thu Dec 11 17:40:20 2008
@@ -37,6 +37,7 @@
 #include "vinagre-bookmarks.h"
 #include "vinagre-ui.h"
 #include "vinagre-window-private.h"
+#include "vinagre-bookmarks-entry.h"
 
 #ifdef VINAGRE_ENABLE_AVAHI
 #include "vinagre-mdns.h"
@@ -53,10 +54,10 @@
 {
   VinagreWindow *window = VINAGRE_WINDOW (object);
 
-  if (window->priv->fav_conn_selected)
+  if (window->priv->fav_entry_selected)
     {
-      g_object_unref (window->priv->fav_conn_selected);
-      window->priv->fav_conn_selected = NULL;
+      g_object_unref (window->priv->fav_entry_selected);
+      window->priv->fav_entry_selected = NULL;
     }
 
   if (window->priv->manager)
@@ -517,17 +518,19 @@
 }
 
 static void
-fav_panel_activated (VinagreFav        *fav,
-		     VinagreConnection *conn,
-		     VinagreWindow     *window)
+fav_panel_activated (VinagreFav            *fav,
+		     VinagreBookmarksEntry *entry,
+		     VinagreWindow         *window)
 {
-  vinagre_cmd_open_bookmark (window, conn);
+  g_return_if_fail (vinagre_bookmarks_entry_get_node (entry) == VINAGRE_BOOKMARKS_ENTRY_NODE_CONN);
+
+  vinagre_cmd_open_bookmark (window, vinagre_bookmarks_entry_get_conn (entry));
 }
 
 static void
-fav_panel_selected (VinagreFav        *fav,
-		    VinagreConnection *conn,
-		    VinagreWindow     *window)
+fav_panel_selected (VinagreFav            *fav,
+		    VinagreBookmarksEntry *entry,
+		    VinagreWindow         *window)
 {
   GtkAction *action1, *action2;
 
@@ -536,15 +539,15 @@
   action2 = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
 					 "BookmarksEdit");
 
-  if (window->priv->fav_conn_selected)
+  if (window->priv->fav_entry_selected)
     {
-      g_object_unref (window->priv->fav_conn_selected);
-      window->priv->fav_conn_selected = NULL;
+      g_object_unref (window->priv->fav_entry_selected);
+      window->priv->fav_entry_selected = NULL;
     }
 
-  if (conn)
+  if (entry)
     {
-      window->priv->fav_conn_selected = vinagre_connection_clone (conn);
+      window->priv->fav_entry_selected = g_object_ref (entry);
       gtk_action_set_sensitive (action1, TRUE);
       gtk_action_set_sensitive (action2, TRUE);
       /* TODO: Change the menuitem label */
@@ -557,17 +560,107 @@
     }
 }
 
+static void
+vinagre_window_populate_bookmarks (VinagreWindow *window,
+				   const gchar   *group,
+				   GSList        *entries,
+				   const gchar   *parent)
+{
+  static guint           i = 0;
+  GSList                *l;
+  VinagreBookmarksEntry *entry;
+  gchar                 *action_name, *action_label, *path, *tooltip;
+  GtkAction             *action;
+  VinagreWindowPrivate  *p = window->priv;
+  VinagreConnection     *conn;
+
+  for (l = entries; l; l = l->next)
+    {
+      entry = VINAGRE_BOOKMARKS_ENTRY (l->data);
+      switch (vinagre_bookmarks_entry_get_node (entry))
+	{
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_FOLDER:
+	    action_label = vinagre_utils_escape_underscores (vinagre_bookmarks_entry_get_name (entry), -1);
+	    action_name = g_strdup_printf ("BOOKMARK_FOLDER_ACTION_%d", ++i);
+	    action = gtk_action_new (action_name,
+				     action_label,
+				     NULL,
+				     NULL);
+	    g_object_set (G_OBJECT (action), "icon-name", "folder", "hide-if-empty", FALSE, NULL);
+	    gtk_action_group_add_action (p->bookmarks_list_action_group,
+					 action);
+	    g_object_unref (action);
+
+	    path = g_strdup_printf ("/MenuBar/BookmarksMenu/%s%s", group, parent?parent:"");
+	    gtk_ui_manager_add_ui (p->manager,
+				   p->bookmarks_list_menu_ui_id,
+				   path,
+				   action_label, action_name,
+				   GTK_UI_MANAGER_MENU,
+				   FALSE);
+	    g_free (path);
+    	    g_free (action_name);
+
+	    path = g_strdup_printf ("%s/%s", parent?parent:"", action_label);
+	    g_free (action_label);
+
+	    vinagre_window_populate_bookmarks (window, group, vinagre_bookmarks_entry_get_children (entry), path);
+	    g_free (path);
+	    break;
+
+	  case VINAGRE_BOOKMARKS_ENTRY_NODE_CONN:
+	    conn = vinagre_bookmarks_entry_get_conn (entry);
+
+	    action_name = vinagre_connection_get_best_name (conn);
+	    action_label = vinagre_utils_escape_underscores (action_name, -1);
+	    g_free (action_name);
+
+	    action_name = g_strdup_printf ("BOOKMARK_ITEM_ACTION_%d", ++i);
+
+	    /* Translators: This is server:port, a statusbar tooltip when mouse is over a bookmark item on menu */
+	    tooltip = g_strdup_printf (_("Open %s:%d"),
+					vinagre_connection_get_host (conn),
+					vinagre_connection_get_port (conn));
+	    action = gtk_action_new (action_name,
+				     action_label,
+				     tooltip,
+				     NULL);
+	    g_object_set (G_OBJECT (action), "icon-name", "application-x-vnc", NULL);
+	    g_object_set_data (G_OBJECT (action), "conn", conn);
+	    gtk_action_group_add_action (p->bookmarks_list_action_group,
+					 action);
+	    g_signal_connect (action,
+			     "activate",
+			     G_CALLBACK (vinagre_cmd_bookmarks_open),
+			     window);
+
+	    path = g_strdup_printf ("/MenuBar/BookmarksMenu/%s%s", group, parent?parent:"");
+	    gtk_ui_manager_add_ui (p->manager,
+				   p->bookmarks_list_menu_ui_id,
+				   path,
+				   action_label, action_name,
+				   GTK_UI_MANAGER_MENUITEM,
+				   FALSE);
+
+	    g_object_unref (action);
+	    g_free (action_name);
+	    g_free (action_label);
+	    g_free (tooltip);
+	    g_free (path);
+	    break;
+
+	  default:
+	    g_assert_not_reached ();
+	}
+    }
+}
+
 void
 vinagre_window_update_bookmarks_list_menu (VinagreWindow *window)
 {
   VinagreWindowPrivate *p = window->priv;
   GList  *actions, *l;
-  GSList *favs;
-#ifdef VINAGRE_ENABLE_AVAHI
-  GSList *mdnss;
-#endif
-  gint   n, m, i;
-  guint  id;
+  GSList *favs, *mdnss = NULL;
 
   g_return_if_fail (p->bookmarks_list_action_group != NULL);
 
@@ -586,118 +679,14 @@
   g_list_free (actions);
 
   favs = vinagre_bookmarks_get_all (vinagre_bookmarks_get_default ());
-  n = g_slist_length (favs);
 
 #ifdef VINAGRE_ENABLE_AVAHI
   mdnss = vinagre_mdns_get_all (vinagre_mdns_get_default ());
-  m = g_slist_length (mdnss);
-#else
-  m = 0;
-#endif
-  i = 0;
-
-  id = (n > 0||m > 0) ? gtk_ui_manager_new_merge_id (p->manager) : 0;
-
-  while (favs)
-    {
-      VinagreConnection *conn;
-      gchar             *action_name, *action_label;
-      GtkAction         *action;
-      gchar             *name, *tooltip;
-
-      conn = (VinagreConnection *) favs->data;
-      g_assert (VINAGRE_IS_CONNECTION (conn));
-
-      name = vinagre_connection_get_best_name (conn);
-
-      action_name = g_strdup_printf ("Bookmark_%d", i);
-      /* Translators: This is server:port, a statusbar tooltip when mouse is over a bookmark item on menu */
-      tooltip = g_strdup_printf (_("Open %s:%d"),
-                                 vinagre_connection_get_host (conn),
-                                 vinagre_connection_get_port (conn));
-      action_label = vinagre_utils_escape_underscores (name, -1);
-      action = gtk_action_new (action_name,
-			       action_label,
-			       tooltip,
-			       NULL);
-      g_object_set (G_OBJECT (action), "icon-name", "application-x-vnc", NULL);
-      g_object_set_data (G_OBJECT (action), "conn", conn);
-      gtk_action_group_add_action (p->bookmarks_list_action_group,
-				   GTK_ACTION (action));
-      g_signal_connect (action,
-			"activate",
-			G_CALLBACK (vinagre_cmd_bookmarks_open),
-			window);
-
-      gtk_ui_manager_add_ui (p->manager,
-			     id,
-			     "/MenuBar/BookmarksMenu/BookmarksList",
-			     action_name, action_name,
-			     GTK_UI_MANAGER_MENUITEM,
-			     FALSE);
-
-      g_object_unref (action);
-      g_free (action_name);
-      g_free (action_label);
-      g_free (name);
-      g_free (tooltip);
-
-      favs = favs->next;
-      i++;
-    }
-
-#ifdef VINAGRE_ENABLE_AVAHI
-  i = 0;
-  while (mdnss)
-    {
-      VinagreConnection *conn;
-      gchar             *action_name, *action_label;
-      GtkAction         *action;
-      gchar             *name, *tooltip;
-
-      conn = (VinagreConnection *) mdnss->data;
-      g_assert (VINAGRE_IS_CONNECTION (conn));
-
-      name = vinagre_connection_get_best_name (conn);
-
-      action_name = g_strdup_printf ("Avahi_%d", i);
-      /* Translators: This is server:port, a statusbar tooltip when mouse is over a bookmark item on menu */
-        tooltip = g_strdup_printf (_("Open %s:%d"),
-                                 vinagre_connection_get_host (conn),
-                                 vinagre_connection_get_port (conn));
-      action_label = vinagre_utils_escape_underscores (name, -1);
-      action = gtk_action_new (action_name,
-			       action_label,
-			       tooltip,
-			       NULL);
-      g_object_set (G_OBJECT (action), "icon-name", "application-x-vnc", NULL);
-      g_object_set_data (G_OBJECT (action), "conn", conn);
-      gtk_action_group_add_action (p->bookmarks_list_action_group,
-				   GTK_ACTION (action));
-      g_signal_connect (action,
-			"activate",
-			G_CALLBACK (vinagre_cmd_bookmarks_open),
-			window);
-
-      gtk_ui_manager_add_ui (p->manager,
-			     id,
-			     "/MenuBar/BookmarksMenu/AvahiList",
-			     action_name, action_name,
-			     GTK_UI_MANAGER_MENUITEM,
-			     FALSE);
-
-      g_object_unref (action);
-      g_free (action_name);
-      g_free (action_label);
-      g_free (name);
-      g_free (tooltip);
-
-      mdnss = mdnss->next;
-      i++;
-    }
 #endif
 
-  p->bookmarks_list_menu_ui_id = id;
+  p->bookmarks_list_menu_ui_id = (g_slist_length (favs) > 0||g_slist_length (mdnss) > 0) ? gtk_ui_manager_new_merge_id (p->manager) : 0;
+  vinagre_window_populate_bookmarks (window, "BookmarksList", favs, NULL);
+  vinagre_window_populate_bookmarks (window, "AvahiList", mdnss, NULL);
 }
 
 static void
@@ -983,7 +972,7 @@
 
   window->priv = VINAGRE_WINDOW_GET_PRIVATE (window);
   window->priv->active_tab = NULL;
-  window->priv->fav_conn_selected = NULL;
+  window->priv->fav_entry_selected = NULL;
   window->priv->fullscreen = FALSE;
   window->priv->signal_notebook = 0;
 



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