Index: admin-tool/profilesdialog.py
===================================================================
--- admin-tool/profilesdialog.py	(revision 746)
+++ admin-tool/profilesdialog.py	(working copy)
@@ -95,6 +95,53 @@
 
         return temp_path
 
+    def __setup_linksdir(self):
+        # FIXME: I should probably use tempfile.mkstemp() here
+
+        # Notes on the variables below:
+        # tmp_linksdir = temporary storage for unsaved global links
+        # tmp_linksdir_real = temporary storage for _saved_ global links
+        # linksdir = real storage directory for the global links
+        self.tmp_linksdir = os.path.join('/tmp', self.profile_name + 'linksdir')
+        self.tmp_linksdir_real = os.path.join('/tmp', self.profile_name + 'linksdir.real')
+        self.linksdir = os.path.join(PROFILESDIR, 'desktop-items', self.profile_name)
+
+        # Delete the temporary directory that may have been 
+        # there from previous sessions
+        if os.access(self.tmp_linksdir, os.F_OK) == True:
+            shutil.rmtree(self.tmp_linksdir)
+        if os.access(self.tmp_linksdir_real, os.F_OK) == True:
+            shutil.rmtree(self.tmp_linksdir_real)
+
+        # Copy the files from the real storage directory to the 
+        # temporary dirs for editing
+        shutil.copytree(self.linksdir, self.tmp_linksdir)
+        shutil.copytree(self.linksdir, self.tmp_linksdir_real) # In case no changes are made
+        shutil.rmtree(self.linksdir)
+        
+        os.chown (self.tmp_linksdir, self.pw.pw_uid, self.pw.pw_gid)
+        os.chown (self.tmp_linksdir_real, self.pw.pw_uid, self.pw.pw_gid)        
+
+        # Temporary measure to fool Nautilus
+        os.symlink(self.tmp_linksdir, self.linksdir)
+        
+
+    def __commit_linksdir(self):
+        os.remove(self.linksdir) # This is a symlink at this stage, so we can do this
+
+        # We would need to copy the saved versions of the desktop links 
+        # to the actual global links directory. So we use the .real suffix.
+        # If confused, look at SessionWindow.__do_save() in sessionwindow.py
+        if os.access(self.tmp_linksdir+'.real', os.F_OK) == True:
+            shutil.copytree(self.tmp_linksdir+'.real', self.linksdir)
+            shutil.rmtree(self.tmp_linksdir+'.real')
+        else:
+            shutil.copytree(self.tmp_linksdir, self.linksdir)
+
+        shutil.rmtree(self.tmp_linksdir)
+        # TODO: See if I need to fiddle with the permissions
+
+
     def __copy_to_user (self, profile_path):
         (fd, user_path) = tempfile.mkstemp (prefix = "profile-%s-" % self.username, suffix = ".zip")
         os.close (fd)
@@ -147,6 +194,8 @@
         protosession.reset_shell_and_homedir (self.username, self.temp_homedir)
         self.temp_homedir = None
 
+        self.__commit_linksdir()
+
         if self.temp_xauth_path:
             os.remove (self.temp_xauth_path)
             self.temp_xauth_path = None
@@ -208,6 +257,9 @@
         self.temp_homedir = protosession.setup_shell_and_homedir (self.username)
         protosession.clobber_user_processes (self.username)
 
+        # Add a temporary directory for storing global items
+        self.__setup_linksdir()
+
         display_number = protosession.find_free_display ()
 
         self.temp_xauth_path = self.__copy_xauthority ()
@@ -448,6 +500,7 @@
             profile_name = model[selected][ProfilesModel.COLUMN_NAME]
             dprint ("Deleting '%s'", profile_name)
             os.remove (_get_profile_path_for_name (profile_name))
+            shutil.rmtree(os.path.join(PROFILESDIR, 'desktop-items', profile_name))
 
             db = userdb.get_database ()
             if db.get_default_profile (False) == profile_name:
@@ -498,13 +551,17 @@
 
     def __create_new_profile (self, profile_name, base_profile):
         profile_name = self.__make_unique_profile_name (profile_name)
+        linksdir = os.path.join(PROFILESDIR, 'desktop-items', profile_name)
 
         if base_profile:
             base_storage = storage.ProfileStorage (base_profile)
             new_storage = base_storage.copy (profile_name)
+            base_linksdir = os.path.join(PROFILESDIR, 'desktop-items', base_profile)
+            shutil.copytree(base_linksdir, linksdir)
         else:
             new_storage = storage.ProfileStorage (profile_name)
             new_storage.save ()
+            os.mkdir(linksdir)
 
         self.profiles_model.reload ()
         row = self.profiles_model.get_iter_first ()
Index: admin-tool/sessionwindow.py
===================================================================
--- admin-tool/sessionwindow.py	(revision 746)
+++ admin-tool/sessionwindow.py	(working copy)
@@ -17,6 +17,8 @@
 #
 
 import time
+import os
+import shutil
 import gobject
 import gtk
 import gtk.gdk
@@ -32,6 +34,7 @@
 import errors
 from lockdownappliersabayon import LockdownApplierSabayon
 from sabayon.lockdown import maindialog as lockdowndialog
+from sabayon.desktoplinks import maindialog as desktoplinksdialog
 from config import *
 
 _ui_string = '''
@@ -45,6 +48,7 @@
     <menu action="EditMenu">
       <menuitem action="Changes"/>
       <menuitem action="Lockdown"/>
+      <menuitem action="DesktopLinks"/>
       <menuitem action="EnforceMandatory"/>
     </menu>
     <menu action="HelpMenu">
@@ -179,6 +183,7 @@
             ("EditMenu",    None,            _("_Edit")),
             ("Changes",     gtk.STOCK_EDIT,  _("_Changes"), "<control>H", _("Edit changes"),             self.__handle_edit),
             ("Lockdown",    None,            _("_Lockdown"),"<control>L", _("Edit Lockdown settings"),   self.__handle_lockdown),
+            ("DesktopLinks",None,            _("_Desktop Links"),"<control>D",_("Edit links on the Desktop"), self.__handle_desktop_links),
             ("HelpMenu",    None,            _("_Help")),
             ("About",       gtk.STOCK_ABOUT, _("_About"),   None,         _("About Sabayon"),            self.__handle_about),
         ]
@@ -241,7 +246,17 @@
                                                              "got an exception while commiting a change")
 
         # Done
+        
+        # Commit the changes to desktop links
+        tmp_linksdir = os.path.join('/tmp', self.profile_name + 'linksdir')
+        tmp_linksdir_real = os.path.join('/tmp', self.profile_name + 'linksdir.real')
 
+        if os.access(tmp_linksdir_real, os.F_OK) == True:
+            shutil.rmtree(tmp_linksdir_real)
+
+        shutil.copytree(tmp_linksdir, tmp_linksdir_real)
+        # Done
+
         self.changes_model.clear ()
         self.profile.sync_changes ()
 
@@ -301,6 +316,13 @@
         self.lockdown_window.window.present ()
 
     @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
+    def __handle_desktop_links (self, action):
+        debuglog.uprint ("Creating new Desktop Link Editor window")
+        self.desktoplinks_window = desktoplinksdialog.EggnogMainDialog (self.profile, 
+                self.profile_name)
+        self.desktoplinks_window.window.present()
+
+    @errors.checked_callback (debuglog.DEBUG_LOG_DOMAIN_USER)
     def __handle_enforce_mandatory (self, action):
         active = action.get_active ()
         debuglog.uprint ("Setting enforce_mandatory to %s", active)
Index: admin-tool/sabayon.glade
===================================================================
--- admin-tool/sabayon.glade	(revision 746)
+++ admin-tool/sabayon.glade	(working copy)
@@ -16,6 +16,7 @@
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
   <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -406,6 +407,7 @@
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
   <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -572,7 +574,7 @@
 <widget class="GtkDialog" id="users_dialog">
   <property name="border_width">6</property>
   <property name="visible">True</property>
-  <property name="title" translatable="no">dialog1</property>
+  <property name="title">dialog1</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
   <property name="window_position">GTK_WIN_POS_NONE</property>
   <property name="modal">False</property>
@@ -586,6 +588,7 @@
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
   <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -724,4 +727,318 @@
   </child>
 </widget>
 
+<widget class="GtkDialog" id="desktop_links_dialog">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Add/Remove Desktop Links</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="gitems_help_button">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-help</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-11</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="gitems_close_button">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-close</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-7</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox4">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkFrame" id="glinks_dirchooser_frame">
+	      <property name="visible">True</property>
+	      <property name="label_xalign">0</property>
+	      <property name="label_yalign">0.5</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment3">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">12</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkHBox" id="hbox2">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">0</property>
+
+		      <child>
+			<widget class="GtkLabel" id="label9">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Choose Directory </property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">2</property>
+			  <property name="expand">False</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkFileChooserButton" id="gitems_dirchooser">
+			  <property name="visible">True</property>
+			  <property name="title" translatable="yes">Select A Directory</property>
+			  <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
+			  <property name="local_only">True</property>
+			  <property name="show_hidden">False</property>
+			  <property name="do_overwrite_confirmation">False</property>
+			  <property name="width_chars">-1</property>
+			</widget>
+			<packing>
+			  <property name="padding">2</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
+			  <property name="pack_type">GTK_PACK_END</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label10">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Mandatory Desktop Links Directory&lt;/b&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">8</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="type">label_item</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkFrame" id="frame4">
+	      <property name="visible">True</property>
+	      <property name="label_xalign">0</property>
+	      <property name="label_yalign">0.5</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment4">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">12</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkVBox" id="vbox5">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">0</property>
+
+		      <child>
+			<widget class="GtkScrolledWindow" id="scrolledwindow1">
+			  <property name="height_request">200</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			  <property name="shadow_type">GTK_SHADOW_IN</property>
+			  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			  <child>
+			    <widget class="GtkTreeView" id="gitemstreeview">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="headers_visible">True</property>
+			      <property name="rules_hint">False</property>
+			      <property name="reorderable">False</property>
+			      <property name="enable_search">True</property>
+			      <property name="fixed_height_mode">False</property>
+			      <property name="hover_selection">False</property>
+			      <property name="hover_expand">False</property>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">3</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkHButtonBox" id="hbuttonbox4">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+			  <property name="spacing">0</property>
+
+			  <child>
+			    <widget class="GtkButton" id="gitemsadd">
+			      <property name="visible">True</property>
+			      <property name="label">gtk-add</property>
+			      <property name="use_stock">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			  </child>
+
+			  <child>
+			    <widget class="GtkButton" id="gitemsremove">
+			      <property name="visible">True</property>
+			      <property name="label">gtk-remove</property>
+			      <property name="use_stock">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			  </child>
+
+			  <child>
+			    <widget class="GtkButton" id="gitemsedit">
+			      <property name="visible">True</property>
+			      <property name="label">gtk-edit</property>
+			      <property name="use_stock">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">4</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label11">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Desktop Links&lt;/b&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">8</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="type">label_item</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
 </glade-interface>
Index: admin-tool/Makefile.am
===================================================================
--- admin-tool/Makefile.am	(revision 746)
+++ admin-tool/Makefile.am	(working copy)
@@ -1,4 +1,5 @@
-SUBDIRS = lockdown
+SUBDIRS = lockdown	\
+		  desktoplinks
 
 PYTHON_FILES =				\
 	editorwindow.py			\
@@ -64,6 +65,7 @@
 
 install-exec-local:
 	mkdir -p $(DESTDIR)$(sysconfdir)/desktop-profiles/
+	mkdir -p $(DESTDIR)$(sysconfdir)/desktop-profiles/desktop-items/
 if CONSOLE_HELPER
 	$(INSTALL_DATA) -D $(srcdir)/sabayon.pam $(DESTDIR)$(PAM_PREFIX)/pam.d/sabayon
 	$(INSTALL_DATA) -D sabayon.console $(DESTDIR)$(PAM_PREFIX)/security/console.apps/sabayon
@@ -83,7 +85,9 @@
 
 pycheck:
 	pychecker -F $(top_srcdir)/pycheckrc $(PYTHON_FILES)
+	(cd desktoplink; make pycheck)
 
+
 clean:
 	rm -f *.pyo *.pyc
 
Index: admin-tool/desktoplinks/itemlist.py
===================================================================
--- admin-tool/desktoplinks/itemlist.py	(revision 0)
+++ admin-tool/desktoplinks/itemlist.py	(revision 0)
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+
+#
+# Copyright (C) 2007 Sayamindu Dasgupta <sayamindu@gnome.org>
+#
+# This is based on code from Pessulus (disabledapplets.py)
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+#
+
+import os
+import dircache
+import exceptions
+import shutil
+from ConfigParser import ConfigParser
+
+import gtk
+import gconf
+import gettext
+
+import icons
+
+
+NAUTILUS_GLOBAL_LINKDIR_ENABLE_KEY = '/apps/nautilus/desktop/global_items_visible'
+
+def insert_unique(l, e):
+    try:
+        l.index(e)
+    except exceptions.ValueError:
+        l.append(e)
+
+# Workaround implementation for g_get_language_names()
+def get_language_names():
+    vars = ['LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG']
+    l = []
+
+    for v in vars:
+        tmp = os.getenv(v)
+        if tmp:
+            tmplist = tmp.split(':')
+            for i in tmplist:
+                language = ""
+                territory = ""
+                codeset = ""
+                modifier = ""
+                if i.find('@') > -1:
+                    modifier = '@'+i.split('@')[1]
+                    i = i.split('@')[0]
+                if i.find('.') > -1:
+                    codeset = '.'+i.split('.')[1]
+                    i = i.split('.')[0]
+                if i.find('_') > -1:
+                    territory = '_'+i.split('_')[1]
+                    i = i.split('_')[0]
+
+                language = i
+                # Now append the various combinations into the list, from the most desirable to the least
+                c = language+territory+codeset+modifier
+                insert_unique(l, c)
+                c = language+territory+modifier
+                insert_unique(l, c)
+                c = language+codeset+modifier
+                insert_unique(l, c)
+                c = language+modifier
+                insert_unique(l, c)
+                c = language+territory+codeset
+                insert_unique(l, c)
+                c = language+territory
+                insert_unique(l, c)
+                c = language+codeset
+                insert_unique(l, c)
+                insert_unique(l, language)
+                #FIXME: There must be a saner way to the above
+
+            l.append('C')
+            return l
+
+    l.append('C')
+    return l
+
+
+class EggnogItemList:
+    (
+        COLUMN_FILENAME,
+        COLUMN_ICON,
+        COLUMN_NAME,
+        COLUMN_MANDATORY
+    ) = range(4)
+
+    def __init__(self, treeview, directory, source):
+        self.directory = directory
+        self.desktop_dir = os.getenv('HOME') + '/Desktop' #FIXME: Is this always true ?
+
+        self.source = source
+
+        client = gconf.client_get_default()
+        self.mandatory_enabled = client.get_bool(NAUTILUS_GLOBAL_LINKDIR_ENABLE_KEY)
+
+        self.liststore = gtk.ListStore (str, gtk.gdk.Pixbuf, str, bool)
+        self.liststore.set_sort_column_id (self.COLUMN_NAME, gtk.SORT_ASCENDING)
+
+        self.treeview = treeview
+        self.treeview.get_selection ().set_mode (gtk.SELECTION_SINGLE)
+        self.treeview.set_model (self.liststore)
+        
+        screen = self.treeview.get_screen ()
+        self.icon_theme = gtk.icon_theme_get_for_screen (screen)
+        #TODO: Add handler for 'changed' signal in icon_theme
+
+        self.__fill_liststore()
+        self.__create_columns()
+
+        self.__update_toggles()
+
+    def refresh(self, directory=None):
+        if directory != None:
+            self.directory = directory
+        self.liststore.clear()
+        self.__fill_liststore()
+        self.__update_toggles()
+
+
+    def __fill_liststore(self):
+        # Make a blank list
+        files = []
+
+        # Make a list of global .desktop files
+        for f in os.listdir(self.directory):
+            files.append(os.path.join(self.directory, f))
+
+        # Make a list of local .desktop files 
+        for f in os.listdir(self.desktop_dir):
+            files.append(os.path.join(self.desktop_dir, f))
+        
+        for i in files:
+            if i.endswith('.desktop') == True:
+                # Set name and icon to some sane defaults 
+                # to handle malformatted .desktop files
+                name = 'Noname'
+                icon = 'gnome-unknown'
+
+                c = ConfigParser()
+                c.read(i)
+                langs = get_language_names()
+                for lang in langs:
+                    if lang == 'C':
+                        option = 'name'
+                    else:
+                        option = ("name[%s]") %lang
+
+                    if c.has_option('Desktop Entry', option) == True:
+                        name = c.get('Desktop Entry', option)
+                        break
+
+                if c.has_option('Desktop Entry', 'icon') == True:
+                    icon = c.get('Desktop Entry', 'icon')
+
+
+                iter = self.liststore.append()
+                self.liststore.set (iter,
+                        self.COLUMN_FILENAME, i,
+                        self.COLUMN_ICON, icons.load_icon (self.icon_theme, icon),
+                        self.COLUMN_NAME, name)
+
+
+    def __create_columns(self):
+        column = gtk.TreeViewColumn ()
+        self.treeview.append_column (column)
+
+        cell = gtk.CellRendererPixbuf ()
+        column.pack_start (cell, False)
+        column.set_attributes (cell, pixbuf = self.COLUMN_ICON)
+
+        column = gtk.TreeViewColumn ('Launcher')
+        self.treeview.append_column (column)
+        
+        cell = gtk.CellRendererText ()
+        column.pack_start (cell, True)
+        column.set_attributes (cell, text = self.COLUMN_NAME)
+
+        column = gtk.TreeViewColumn('Mandatory')
+        self.treeview.append_column (column)
+
+        cell = gtk.CellRendererToggle ()
+        cell.connect ("toggled", self.__on_toggled)
+        column.pack_start (cell, True)
+        column.set_attributes (cell, active = self.COLUMN_MANDATORY)
+
+    def __on_toggled(self, toggle, path):
+        def toggle_value (model, iter, column):
+            model[iter][column] = not model[iter][column]
+            return model[iter][column]
+
+        iter = self.liststore.get_iter (path)
+        active = toggle_value (self.liststore, iter, self.COLUMN_MANDATORY)
+
+        if active == True and self.mandatory_enabled == False:
+            self.source.set_gconf_boolean (NAUTILUS_GLOBAL_LINKDIR_ENABLE_KEY,
+                    True, True)
+            self.mandatory_enabled = True
+
+        #FIXME: The code below works only for Python 2.3 and later
+        # The code below swaps parent directories of the .desktop file
+        if self.liststore[iter][self.COLUMN_FILENAME].startswith(self.directory) == True:
+            shutil.move(self.liststore[iter][self.COLUMN_FILENAME], self.desktop_dir)
+            self.liststore.set (iter, self.COLUMN_FILENAME, 
+                    os.path.join(self.desktop_dir, 
+                        os.path.basename(self.liststore[iter][self.COLUMN_FILENAME])))
+        else:
+            shutil.move(self.liststore[iter][self.COLUMN_FILENAME], self.directory)
+            self.liststore.set(iter, self.COLUMN_FILENAME, 
+                    os.path.join(self.directory, 
+                        os.path.basename(self.liststore[iter][self.COLUMN_FILENAME])))
+
+
+    def __update_toggles (self):
+        def update_toggle (model, path, iter, data):
+            mandatory = model[iter][self.COLUMN_FILENAME].startswith(self.directory)
+            if model[iter][self.COLUMN_MANDATORY] != mandatory:
+                model[iter][self.COLUMN_MANDATORY] = mandatory
+
+        self.liststore.foreach (update_toggle, self)
+        self.treeview.set_sensitive (os.access(self.directory, os.W_OK)) 
+
+    def get_filename_from_selection (self, selection):
+        iter = selection.get_selected()[1]
+        return self.liststore[iter][self.COLUMN_FILENAME]
Index: admin-tool/desktoplinks/icons.py
===================================================================
--- admin-tool/desktoplinks/icons.py	(revision 0)
+++ admin-tool/desktoplinks/icons.py	(revision 0)
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+# vim: set ts=4 sw=4 et:
+
+#
+# Copyright (C) 2005 Vincent Untz <vuntz@gnome.org>
+#
+# This is based on code from gnome-menus (in menutreemodel.py)
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+#
+
+import os
+import gtk
+
+def load_icon_from_path (icon_path):
+    if os.path.isfile (icon_path):
+        try:
+            return gtk.gdk.pixbuf_new_from_file_at_size (icon_path, 24, 24)
+        except:
+            pass
+    return None
+
+def load_icon_from_data_dirs (icon_value):
+    data_dirs = None
+    if os.environ.has_key ("XDG_DATA_DIRS"):
+        data_dirs = os.environ["XDG_DATA_DIRS"]
+    if not data_dirs:
+        data_dirs = "/usr/local/share/:/usr/share/"
+
+    for data_dir in data_dirs.split (":"):
+        retval = load_icon_from_path (os.path.join (data_dir, "pixmaps",
+                                                    icon_value))
+        if retval:
+            return retval
+        retval = load_icon_from_path (os.path.join (data_dir, "icons",
+                                                    icon_value))
+        if retval:
+            return retval
+    
+    return None
+
+def load_icon (icon_theme, icon_value):
+    if not icon_value:
+        return
+
+    if os.path.isabs (icon_value):
+        icon = load_icon_from_path (icon_value)
+        if icon:
+            return icon
+        icon_name = os.path.basename (icon_value)
+    else:
+        icon_name = icon_value
+    
+    if icon_name.endswith (".png"):
+        icon_name = icon_name[:-len (".png")]
+    elif icon_name.endswith (".xpm"):
+        icon_name = icon_name[:-len (".xpm")]
+    elif icon_name.endswith (".svg"):
+        icon_name = icon_name[:-len (".svg")]
+    
+    try:
+        return icon_theme.load_icon (icon_name, 24, 0)
+    except:
+        return load_icon_from_data_dirs (icon_value)
Index: admin-tool/desktoplinks/__init__.py
===================================================================
Index: admin-tool/desktoplinks/Makefile.am
===================================================================
--- admin-tool/desktoplinks/Makefile.am	(revision 0)
+++ admin-tool/desktoplinks/Makefile.am	(revision 0)
@@ -0,0 +1,22 @@
+PYTHON_FILES =				\
+	icons.py  \
+	itemlist.py  \
+	maindialog.py \
+	__init__.py
+
+NOINST_PYTHON_FILES =			\
+	testsource.py
+
+EXTRA_DIST =				\
+	$(PYTHON_FILES)			\
+	$(NOINST_PYTHON_FILES)
+
+desktoplinksdir     = $(pyexecdir)/sabayon/desktoplinks
+desktoplinks_PYTHON = $(PYTHON_FILES)
+
+pycheck:
+	pychecker -F $(top_srcdir)/pycheckrc $(PYTHON_FILES) $(NOINST_PYTHON_FILES)
+
+clean:
+	rm -f *.pyo *.pyc
+	rm -f $(BUILT_SOURCES)
Index: admin-tool/desktoplinks/maindialog.py
===================================================================
--- admin-tool/desktoplinks/maindialog.py	(revision 0)
+++ admin-tool/desktoplinks/maindialog.py	(revision 0)
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+#
+# Copyright (C) 2007 Sayamindu Dasgupta <sayamindu@gnome.org>
+#
+# This is based on code from Pessulus (maindialog.py)
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+#
+
+
+import gconf
+import gettext
+import gobject
+import gtk
+import gtk.glade
+import gnome
+
+import os
+import random
+import subprocess 
+
+import itemlist
+
+from sabayon.config import *
+
+from sabayon import errors
+from sabayon import debuglog
+
+NAUTILUS_GLOBAL_LINKDIR_KEY = '/apps/nautilus/desktop/global_items_dir'
+
+def gen_new_filename(dir):
+    while True:
+        append = random.random()
+        basename = 'globalitem-' + str(append)[2:] + '.desktop'
+        if not os.path.isfile(os.path.join(dir, basename)):
+            break
+
+    print basename
+
+    return os.path.join(dir, basename)
+
+# TODO: Implement standalone support 
+
+class EggnogMainDialog:
+    def __init__(self, profile, profile_name, standalone = False):
+        
+        self.profile = profile
+        self.profile_name = profile_name
+        self.standalone = standalone
+
+        self.directory = os.path.join('/tmp', self.profile_name + 'linksdir')
+        self.source = profile.get_source ("GConf")
+
+        real_directory = os.path.join(PROFILESDIR, 'desktop-items', self.profile_name)
+        self.source.set_gconf_string(NAUTILUS_GLOBAL_LINKDIR_KEY, real_directory, True)
+
+        self.glade_file = os.path.join (GLADEDIR, "sabayon.glade")
+        self.xml = gtk.glade.XML (self.glade_file, "desktop_links_dialog")
+
+        # Get all the widgets
+        self.window = self.xml.get_widget ("desktop_links_dialog")
+        
+        self.global_checkbutton = self.xml.get_widget ("gitems_checkbutton")
+        self.global_dirchooser =  self.xml.get_widget ("gitems_dirchooser")
+        self.global_treeview =  self.xml.get_widget ("gitemstreeview")
+        self.global_item_add =  self.xml.get_widget ("gitemsadd")
+        self.global_item_remove =  self.xml.get_widget ("gitemsremove")
+        self.global_item_edit = self.xml.get_widget ("gitemsedit")
+        self.help_button = self.xml.get_widget ("gitems_help_button")
+
+        dirchooser_frame = self.xml.get_widget("glinks_dirchooser_frame")
+        if self.standalone == False:
+            dirchooser_frame.hide_all()
+            self.help_button.hide_all()
+            self.window.connect ("delete-event", gtk.Widget.hide_on_delete)
+        else:
+            self.window.connect ("destroy", self.__on_dialog_destroy)
+
+        # Setup the signal handlers
+        self.window.connect ("response", self.__on_dialog_response)
+        self.global_item_add.connect('clicked',
+                self.__on_item_add_clicked)
+        self.global_item_remove.connect('clicked',
+                self.__on_item_remove_clicked)
+        self.global_item_edit.connect('clicked',
+                self.__on_item_edit_clicked)
+
+        # Setup the list
+        self.__init_list()
+
+        self.window.show ()
+
+    def __init_list(self):
+        self.item_list = itemlist.EggnogItemList(self.global_treeview, 
+                self.directory, self.source)
+
+    def __wait_for_item_editor(self, process):
+        if process.poll() != None:
+            # This means that the user has cloase the 
+            # gnome-desktop-item-edit window
+            self.item_list.refresh()
+            return False
+        #FIXME: Should I disable the Add/Remove/Edit buttons here ?
+        return True
+
+    def __on_dialog_destroy (self, dialog):
+         gtk.main_quit ()
+
+    def __on_dialog_response (self, dialog, response_id):
+        if dialog == self.window and response_id == gtk.RESPONSE_CLOSE:
+             dialog.hide_all ()
+             if self.standalone == True:
+                 dialog.destroy ()
+
+
+    def __on_item_edit_clicked(self, button):
+        selection = self.global_treeview.get_selection()
+        filename = self.item_list.get_filename_from_selection(selection)
+        # Code below is stolen from Alacarte
+        process = subprocess.Popen(['gnome-desktop-item-edit', filename], env=os.environ)
+        gobject.timeout_add(100, self.__wait_for_item_editor, process)
+
+    def __on_item_remove_clicked(self, button):
+        selection = self.global_treeview.get_selection()
+        filename = self.item_list.get_filename_from_selection(selection)
+        os.remove(filename) #TODO: Add a confirmation dialog 
+        self.item_list.refresh()
+
+    def __on_item_add_clicked(self, button):
+        # By default, all items that are created are non-mandatory
+        desktop_dir = os.getenv('HOME') + '/Desktop'
+        filename = gen_new_filename(desktop_dir)
+        process = subprocess.Popen(['gnome-desktop-item-edit', filename], env=os.environ)
+        gobject.timeout_add(100, self.__wait_for_item_editor, process)
+
Index: admin-tool/desktoplinks/.cvsignore
===================================================================
--- admin-tool/desktoplinks/.cvsignore	(revision 0)
+++ admin-tool/desktoplinks/.cvsignore	(revision 0)
@@ -0,0 +1,4 @@
+Makefile
+Makefile.in
+*.pyc
+*~
Index: configure.ac
===================================================================
--- configure.ac	(revision 746)
+++ configure.ac	(working copy)
@@ -249,6 +249,7 @@
 doc/Makefile
 admin-tool/Makefile
 admin-tool/sabayon.console
+admin-tool/desktoplinks/Makefile
 admin-tool/lockdown/Makefile
 po/Makefile.in
 ])
Index: lib/sources/gconfsource.py
===================================================================
--- lib/sources/gconfsource.py	(revision 746)
+++ lib/sources/gconfsource.py	(working copy)
@@ -344,6 +344,14 @@
         self.client.set_bool (key, value)
         self.emit_change (change)
 
+    def set_gconf_string (self, key, value, mandatory):
+        gconf_value = gconf.Value (gconf.VALUE_STRING)
+        gconf_value.set_string (value)
+        change = GConfChange (self, key, gconf_value)
+        change.set_mandatory (mandatory)
+        self.client.set_string (key, value)
+        self.emit_change (change)
+
     def set_gconf_list (self, key, list_type, value, mandatory):
         gconf_value = gconf.Value (gconf.VALUE_LIST)
         list = []
