[gnome-control-center] Copy libslab from gnome-main-menu, for not breaking 2.26.x



commit d35675191b70a94f0d2b4a945d4638514c84e49e
Author: Rodrigo Moya <rodrigo gnome-db org>
Date:   Mon Jun 29 18:15:07 2009 +0200

    Copy libslab from gnome-main-menu, for not breaking 2.26.x

 Makefile.am                        |    4 +-
 configure.in                       |   15 +-
 libslab/.cvsignore                 |    8 +
 libslab/ChangeLog                  |  248 +++++++
 libslab/Makefile.am                |  105 +++
 libslab/app-resizer.c              |  342 +++++++++
 libslab/app-resizer.h              |   70 ++
 libslab/app-shell-startup.c        |  123 ++++
 libslab/app-shell-startup.h        |   37 +
 libslab/app-shell.c                | 1407 ++++++++++++++++++++++++++++++++++++
 libslab/app-shell.h                |  140 ++++
 libslab/application-tile.c         |  882 ++++++++++++++++++++++
 libslab/application-tile.h         |   66 ++
 libslab/bookmark-agent.c           | 1238 +++++++++++++++++++++++++++++++
 libslab/bookmark-agent.h           |   89 +++
 libslab/directory-tile.c           |  669 +++++++++++++++++
 libslab/directory-tile.h           |   57 ++
 libslab/document-tile.c            | 1093 ++++++++++++++++++++++++++++
 libslab/document-tile.h            |   63 ++
 libslab/double-click-detector.c    |  122 ++++
 libslab/double-click-detector.h    |   58 ++
 libslab/gnome-utils.c              |  346 +++++++++
 libslab/gnome-utils.h              |   40 +
 libslab/libslab-utils.c            |  695 ++++++++++++++++++
 libslab/libslab-utils.h            |   39 +
 libslab/libslab.pc.in              |   12 +
 libslab/nameplate-tile.c           |  291 ++++++++
 libslab/nameplate-tile.h           |   55 ++
 libslab/nld-marshal.list           |    1 +
 libslab/recent-files.c             |  294 ++++++++
 libslab/recent-files.h             |   80 ++
 libslab/search-bar.c               |  361 +++++++++
 libslab/search-bar.h               |   72 ++
 libslab/search-context-picker.c    |  196 +++++
 libslab/search-context-picker.h    |   58 ++
 libslab/search-entry-watermark.svg |   58 ++
 libslab/search-entry.c             |  145 ++++
 libslab/search-entry.h             |   50 ++
 libslab/shell-window.c             |  187 +++++
 libslab/shell-window.h             |   66 ++
 libslab/slab-gnome-util.c          |  474 ++++++++++++
 libslab/slab-gnome-util.h          |   76 ++
 libslab/slab-section.c             |  197 +++++
 libslab/slab-section.h             |   71 ++
 libslab/system-tile.c              |  286 ++++++++
 libslab/system-tile.h              |   53 ++
 libslab/themed-icon.c              |  165 +++++
 libslab/themed-icon.h              |   53 ++
 libslab/tile-action.c              |  108 +++
 libslab/tile.c                     |  621 ++++++++++++++++
 libslab/tile.h                     |  143 ++++
 51 files changed, 12122 insertions(+), 7 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index aa53224..25d9aaa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
 SUBDIRS = po libwindow-settings capplets font-viewer help
 
-if HAVE_LIBSLAB
-SUBDIRS += shell
+if HAVE_LIBSLAB_DEPS
+SUBDIRS += libslab shell
 endif
 
 if HAVE_TYPING_BREAK
diff --git a/configure.in b/configure.in
index 246e569..68cb534 100644
--- a/configure.in
+++ b/configure.in
@@ -110,16 +110,19 @@ GTK_ENGINE_DIR="$gtk_lib_dir/gtk-2.0/$gtk_binary_version/engines"
 AC_SUBST(GTK_ENGINE_DIR)
 
 dnl
-dnl Check if we have libslab
+dnl Check dependencies of libslab
 dnl
 PKG_CHECK_MODULES(LIBSLAB, [
-			   libslab libgnomeui-2.0 gnome-desktop-2.0 librsvg-2.0 libgnome-menu pango libgnomeui-2.0
+			   libgnomeui-2.0 gnome-desktop-2.0 librsvg-2.0 libgnome-menu pango libgnomeui-2.0
 			   ],
-			   have_libslab=yes,
-			   have_libslab=no)
+			   have_libslab_deps=yes,
+			   have_libslab_deps=no)
 AC_SUBST(LIBSLAB_CFLAGS)
 AC_SUBST(LIBSLAB_LIBS)
-AM_CONDITIONAL(HAVE_LIBSLAB, [test $have_libslab = yes])
+AM_CONDITIONAL(HAVE_LIBSLAB_DEPS, [test $have_libslab_deps = yes])
+
+ENABLE_DYNAMIC_LIBSLAB=0
+AM_CONDITIONAL(ENABLE_DYNAMIC_LIBSLAB, test "x$ENABLE_DYNAMIC_LIBSLAB" = "x1")
 
 dnl
 dnl Check for Xft version 2; we build in extra functionality to the font capplet
@@ -324,6 +327,8 @@ help/Makefile
 libwindow-settings/Makefile
 libwindow-settings/gnome-window-settings-2.0.pc
 po/Makefile.in
+libslab/Makefile
+libslab/libslab.pc
 shell/Makefile
 shell/gnomecc.desktop.in
 typing-break/Makefile
diff --git a/libslab/.cvsignore b/libslab/.cvsignore
new file mode 100644
index 0000000..8f5fb91
--- /dev/null
+++ b/libslab/.cvsignore
@@ -0,0 +1,8 @@
+.deps
+.libs
+Makefile
+Makefile.in
+nld-marshal.c
+nld-marshal.h
+search-entry-watermark.h
+libslab.pc
diff --git a/libslab/ChangeLog b/libslab/ChangeLog
new file mode 100644
index 0000000..57ca949
--- /dev/null
+++ b/libslab/ChangeLog
@@ -0,0 +1,248 @@
+=== ChangeLog discontinued ===
+
+	With the move to Git, gnome-main-menu is switching from a ChangeLog file
+	to relying on commit messages to provide change history.
+
+2009-01-07  Magnus Boman  <captain magnus gmail com>
+
+	reviewed by: Magnus Boman
+
+	* app-resizer.c:
+	* app-resizer.h:
+	* app-shell.c (delete_old_data):
+	* app-shell.h:
+	* application-tile.c (application_tile_setup):
+	* directory-tile.c (directory_tile_new):
+	* document-tile.c (document_tile_new):
+	* nameplate-tile.c (nameplate_tile_class_init),
+	(nameplate_tile_get_property), (nameplate_tile_set_property):
+	* search-bar.h:
+	* search-context-picker.c:
+	* search-context-picker.h:
+	- Use single GTK inclues - Fix bgo#551850#c2
+	  Patch by Cosimo Cecchi
+	- Compile with G*_DISABLE_DEPRECATED - Fixes bgo#551850#c3
+	  Patch by Cosimo Cecchi
+
+==================== 0.9.12 ====================
+
+2008-11-06  Federico Mena Quintero  <federico novell com>
+
+	Use the new GtkTooltip mechanism rather than the deprecated
+	GtkTooltips.  This also fixes a leak of GtkTooltips objects.
+
+	* nameplate-tile.c (NameplateTilePrivate): Removed the "tooltips" field.
+	(nameplate_tile_set_property): Use gtk_widget_set_tooltip_text()
+	rather than creating a GtkTooltips object and setting the tooltips there.
+	(nameplate_tile_get_property): Use gtk_widget_get_tooltip_text()
+	instead of getting a GtkTooltipsData object.
+
+2008-11-06  Federico Mena Quintero  <federico novell com>
+
+	* bookmark-agent.c (create_app_item): Free the title of the
+	bookmark file.
+
+2008-10-27  Federico Mena Quintero  <federico novell com>
+
+	Fix some memory leaks:
+
+	* tile.c (tile_finalize): Actually destroy the context menu, don't
+	ref/sink it.
+	(tile_set_property): Attach the context menu to the tile, don't
+	just store its pointer.
+
+	* nameplate-tile.c (nameplate_tile_finalize): Free the tooltips.
+
+	* bookmark-agent.c (create_app_item): Free the URI.
+	(create_doc_item): Likewise.
+	(create_dir_item): Likewise.
+
+2008-09-22  Scott Reeves  <sreeves novell com>
+	* document-tile.c:
+	show remote docs - use gio for tooltip, show icon
+
+==================== 0.9.11 ====================
+
+2008-09-07  Cosimo Cecchi  <cosimoc gnome org>
+
+	* app-shell.c: (show_no_results_message):
+	Escape the markup text properly.
+	Patch by Denis Washington (#518749).
+
+2008-08-22  Cosimo Cecchi  <cosimoc gnome org>
+
+	* Makefile.am:
+	* app-shell.c: (generate_new_apps):
+	* bookmark-agent.c: (bookmark_agent_move_item),
+	(bookmark_agent_remove_item), (bookmark_agent_new), (finalize),
+	(update_user_spec_path), (store_monitor_cb),
+	(recent_item_mru_comp_func):
+	* directory-tile.c: (directory_tile_new),
+	(directory_tile_private_setup), (directory_tile_finalize),
+	(rename_entry_activate_cb), (move_to_trash_trigger),
+	(delete_trigger), (open_with_default_trigger):
+	* document-tile.c: (document_tile_new),
+	(document_tile_private_setup), (document_tile_finalize),
+	(rename_entry_activate_cb), (open_with_default_trigger),
+	(move_to_trash_trigger), (delete_trigger), (user_docs_trigger):
+	* recent-files.c: (main_menu_recent_monitor_init),
+	(main_menu_recent_file_init), (main_menu_recent_monitor_finalize),
+	(main_menu_recent_file_finalize), (main_menu_recent_monitor_new),
+	(get_files), (main_menu_recent_file_get_uri),
+	(main_menu_recent_file_get_mime_type),
+	(main_menu_recent_file_get_modified),
+	(main_menu_rename_recent_file), (main_menu_remove_recent_file),
+	(recent_file_store_monitor_cb), (recent_item_mru_comp_func):
+	* slab-gnome-util.c: (copy_file):
+	Port to GIO and drop gnome-vfs dependency (#527903).
+
+2008-08-11  A. Walton  <awalton gnome org>
+
+	reviewed by: Federico Mena Quintero
+
+	* directory-tile.c (delete_trigger):
+	* document-tile.c (delete_trigger):
+	Remove Eel dependency from libslab. Bug #524778.
+
+2008-07-28  Federico Mena Quintero  <federico novell com>
+
+	https://bugzilla.novell.com/show_bug.cgi?id=402256 - Main-menu has
+	high CPU usage on NIS/NFS home directories.
+
+	* document-tile.c (load_image): Don't do thumbnailing ourselves,
+	as we don't have the threading logic to do it in the background.
+	Instead, let Nautilus handle thumbnailing by itself.
+
+2008-07-24  Cosimo Cecchi  <cosimoc gnome org>
+
+	http://bugzilla.gnome.org/show_bug.cgi?id=544538
+
+	* app-shell.c: (generate_launchers):
+	Fix a build warning.
+
+==================== 0.9.10 ====================
+
+2008-05-13  Federico Mena Quintero  <federico novell com>
+
+	* application-tile.c (application_tile_setup): Ensure that name
+	and desc are non-NULL before comparing them.
+
+2008-05-09  Magnus Boman  <captain magnus gmail com>
+
+        https://bugzilla.novell.com/show_bug.cgi?id=224885 -
+        Don't display both generic name and description if they are the same.
+
+        * libslab/application-tile.c (application_tile_setup): Set subheader
+        to NULL if description is the same as generic name.
+
+2008-04-28  Scott Reeves  <sreeves novell com>
+
+	* bookmark-agent.c:
+	Fix for BNC#308998 - missing translation in menu
+
+2008-04-23  Federico Mena Quintero  <federico novell com>
+
+	* libslab-utils.c (create_thumbnail_factory): Continue the fix for
+	the last bug; when a second instance of the applet gets added, it
+	will want to initialize the thumbnail factory again.
+
+2008-04-23  Federico Mena Quintero  <federico novell com>
+
+	https://bugzilla.novell.com/show_bug.cgi?id=380339 - Crash when
+	there are two main-menu applets.
+
+	* libslab-utils.c (libslab_thumbnail_factory_preinit): Check if
+	the idle handler is already set up, so we don't try to initialize
+	the factory twice if there is more than one main-menu applet
+	(e.g. for multiple monitors).
+
+2008-04-09  Federico Mena Quintero  <federico novell com>
+
+	* bookmark-agent.c (create_doc_item): Don't leak the template
+	filename.
+
+2008-04-08  Federico Mena Quintero  <federico novell com>
+
+	* bookmark-agent.c (bookmark_agent_remove_item): Free the uris we
+	got from the GBookmarkFile.
+	(load_xbel_store): Likewise.
+
+2008-04-04  Federico Mena Quintero  <federico novell com>
+
+	* libslab-utils.c (libslab_checkpoint_init): New public function;
+	initializes a checkpoint file.
+	(libslab_checkpoint): New public function to add checkpoints to
+	the code for profiling purposes.
+	(libslab_thumbnail_factory_preinit): New public function to queue
+	the initialization of the thumbnail factory in an idle handler.
+	With this we can create the thumbnail factory between the applet
+	hitting the main loop initially and the user activating the
+	Computer menu.
+	(libslab_thumbnail_factory_get): New public function.
+
+	* bookmark-agent.c (bookmark_agent_update_from_bookmark_file): New
+	public function; allows the caller to update a BookmarkAgent
+	explicitly from a given GBookmarkFile.  This makes the
+	BookmarkAgent not monitor the recent-files store on its own, to
+	give more control to the caller.
+
+	* document-tile.c (document_tile_new): Don't call load_image()
+	here; it already gets done in document_tile_style_set().
+	(load_image): Use libslab_thumbnail_factory_get() to use the
+	global thumbnail factory, instead of one of our own.
+
+2008-03-10  Rodrigo Moya <rodrigo gnome-db org>
+
+	* Makefile.am: only set libtool versioning when building dynamic lib.
+
+==================== 0.9.9 ====================
+
+2008-02-17  Jens Granseuer  <jensgr gmx net>
+
+	* slab-gnome-util.c: (get_slab_gconf_bool), (get_slab_gconf_int),
+	(get_slab_gconf_string), (free_list_of_strings),
+	(free_slab_gconf_slist_of_strings),
+	(get_package_name_from_desktop_item): fix a few leaks and don't
+	open-code g_(s)list_foreach
+
+2008-02-08  Federico Mena Quintero  <federico novell com>
+
+	* Makefile.am (libslab_la_LDFLAGS): Specify libtool versioning
+	info.  Copied from libgnomeui/libgnomeui/Makefile.am.
+
+2007-09-16  Andre Klapper  <a9016009 gmx de>
+
+	* system-tile.c: revert to svn revision 339 (=before
+	GNOME string freeze break).
+
+2007-02-12  Rodrigo Moya <rodrigo gnome-db org>
+
+	* application-tile.c (application_tile_setup): check strings before
+	calling g_str_has_prefix().
+
+2007-02-12  Damien Carbery <damien carbery sun com>
+
+	Fixes #383022
+
+	* document-tile.c:
+	* gnome-utils.c: use more portable G_GNUC_FUNCTION instead of
+	__FUNCTION__.
+
+2007-02-06  Rodrigo Moya <rodrigo gnome-db org>
+
+	* application-tile.[ch] (application_tile_new_full): added 'gconf_prefix'
+	argument, so that the AppTile object is able to disable context menu
+	items based on the presence of some GConf keys.
+	(application_tile_set_property): free previous values for not leaking.
+
+	* app-shell.c (insert_launcher_into_category): pass the GConf prefix we
+	get from the application to the App tiles.
+
+2007-02-01  Scott Reeves  <sreeves novell com>
+	* application-tile.c: small fixup of previous patch to not try and
+	add the menu item when no help available.
+
+2007-02-01  Rodrigo Moya <rodrigo gnome-db org>
+
+	* application-tile.c (application_tile_setup): don't show the 'Help
+	unavailable' item when there is no documentation.
diff --git a/libslab/Makefile.am b/libslab/Makefile.am
new file mode 100644
index 0000000..dc182c6
--- /dev/null
+++ b/libslab/Makefile.am
@@ -0,0 +1,105 @@
+INCLUDES =					\
+	$(LIBSLAB_CFLAGS)			\
+	$(WARN_CFLAGS)
+
+HEADER_FILES=					\
+	$(BUILT_SOURCES)			\
+	app-resizer.h				\
+	app-shell.h				\
+	app-shell-startup.h			\
+	application-tile.h			\
+	directory-tile.h			\
+	document-tile.h				\
+	gnome-utils.h				\
+	nameplate-tile.h			\
+	search-bar.h				\
+	search-context-picker.h			\
+	search-entry.h				\
+	shell-window.h				\
+	slab-gnome-util.h			\
+	slab-section.h				\
+	system-tile.h				\
+	tile.h
+
+if ENABLE_DYNAMIC_LIBSLAB
+lib_LTLIBRARIES = libslab.la
+SOURCE_HEADER_FILES=
+else
+noinst_LTLIBRARIES = libslab.la
+SOURCE_HEADER_FILES=$(HEADER_FILES)
+endif
+
+libslab_la_SOURCES =				\
+	$(SOURCE_HEADER_FILES)			\
+	$(MARSHAL_GENERATED)			\
+	app-resizer.c				\
+	app-shell.c				\
+	app-shell-startup.c			\
+	application-tile.c			\
+	bookmark-agent.h			\
+	bookmark-agent.c			\
+	directory-tile.c			\
+	document-tile.c				\
+	double-click-detector.c			\
+	double-click-detector.h			\
+	gnome-utils.c				\
+	libslab-utils.c				\
+	libslab-utils.h				\
+	nameplate-tile.c			\
+	search-bar.c				\
+	search-context-picker.c			\
+	search-entry-watermark.h		\
+	search-entry.c				\
+	shell-window.c				\
+	slab-gnome-util.c			\
+	slab-section.c				\
+	system-tile.c				\
+	themed-icon.c				\
+	themed-icon.h				\
+	tile-action.c				\
+	tile.c
+
+if ENABLE_DYNAMIC_LIBSLAB
+libslab_includedir = $(includedir)/slab
+libslab_include_HEADERS = $(HEADER_FILES)
+libslab_la_LDFLAGS = \
+	-version-info $(LT_VERSION)
+endif
+
+libslab_la_LIBADD = $(LIBSLAB_LIBS)
+
+if ENABLE_DYNAMIC_LIBSLAB
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libslab.pc
+endif
+
+search-entry-watermark.h: search-entry-watermark.svg
+	echo '#define SEARCH_ENTRY_WATERMARK_SVG "\' > $@; \
+	sed -e 's/"/\\"/g' -e 's/$$/\\/' -e 's/#000000/#%s/g' $< >> $@; \
+	echo '"' >> $@
+
+
+MARSHAL_GENERATED = nld-marshal.c nld-marshal.h
+
+nld-marshal.h: nld-marshal.list
+	( @GLIB_GENMARSHAL@ --prefix=nld_marshal $(srcdir)/nld-marshal.list --header > nld-marshal.tmp \
+	&& mv nld-marshal.tmp nld-marshal.h ) \
+	|| ( rm -f nld-marshal.tmp && exit 1 )
+
+nld-marshal.c: nld-marshal.h
+	( (echo '#include "nld-marshal.h"'; @GLIB_GENMARSHAL@ --prefix=nld_marshal $(srcdir)/nld-marshal.list --body) > nld-marshal.tmp \
+	&& mv nld-marshal.tmp nld-marshal.c ) \
+	|| ( rm -f nld-marshal.tmp && exit 1 )
+
+
+BUILT_SOURCES =				\
+	search-entry-watermark.h	\
+	$(MARSHAL_GENERATED)
+
+CLEANFILES =				\
+	search-entry-watermark.h	\
+	$(MARSHAL_GENERATED)
+
+EXTRA_DIST=				\
+	search-entry-watermark.svg	\
+	nld-marshal.list
diff --git a/libslab/app-resizer.c b/libslab/app-resizer.c
new file mode 100644
index 0000000..44f7b02
--- /dev/null
+++ b/libslab/app-resizer.c
@@ -0,0 +1,342 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-desktop-item.h>
+
+#include "app-shell.h"
+#include "app-resizer.h"
+
+static GtkLayoutClass *parent_class = NULL;
+
+static void app_resizer_class_init (AppResizerClass *);
+static void app_resizer_init (AppResizer *);
+static void app_resizer_destroy (GtkObject *);
+
+static void app_resizer_size_allocate (GtkWidget * resizer, GtkAllocation * allocation);
+static gboolean app_resizer_paint_window (GtkWidget * widget, GdkEventExpose * event,
+	AppShellData * app_data);
+
+GType
+app_resizer_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info = {
+			sizeof (AppResizerClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) app_resizer_class_init,
+			NULL,
+			NULL,
+			sizeof (AppResizer),
+			0,
+			(GInstanceInitFunc) app_resizer_init
+		};
+
+		object_type =
+			g_type_register_static (GTK_TYPE_LAYOUT, "AppResizer", &object_info, 0);
+	}
+
+	return object_type;
+}
+
+static void
+app_resizer_class_init (AppResizerClass * klass)
+{
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	((GtkObjectClass *) klass)->destroy = app_resizer_destroy;
+
+	widget_class = GTK_WIDGET_CLASS (klass);
+	widget_class->size_allocate = app_resizer_size_allocate;
+}
+
+static void
+app_resizer_init (AppResizer * window)
+{
+}
+
+void
+remove_container_entries (GtkContainer * widget)
+{
+	GList *children, *l;
+
+	children = gtk_container_get_children (widget);
+	for (l = children; l; l = l->next)
+	{
+		GtkWidget *child = GTK_WIDGET (l->data);
+		gtk_container_remove (GTK_CONTAINER (widget), GTK_WIDGET (child));
+	}
+
+	if (children)
+		g_list_free (children);
+}
+
+static void
+resize_table (GtkTable * table, gint columns, GList * launcher_list)
+{
+	float rows, remainder;
+
+	remove_container_entries (GTK_CONTAINER (table));
+
+	rows = ((float) g_list_length (launcher_list)) / (float) columns;
+	remainder = rows - ((int) rows);
+	if (remainder != 0.0)
+		rows += 1;
+
+	gtk_table_resize (table, (int) rows, columns);
+}
+
+static void
+relayout_table (GtkTable * table, GList * element_list)
+{
+	gint maxcols = (GTK_TABLE (table))->ncols;
+	gint row = 0, col = 0;
+	do
+	{
+		GtkWidget *element = GTK_WIDGET (element_list->data);
+		gtk_table_attach (table, element, col, col + 1, row, row + 1, GTK_EXPAND | GTK_FILL,
+			GTK_EXPAND | GTK_FILL, 0, 0);
+		col++;
+		if (col == maxcols)
+		{
+			col = 0;
+			row++;
+		}
+	}
+	while (NULL != (element_list = g_list_next (element_list)));
+}
+
+void
+app_resizer_layout_table_default (AppResizer * widget, GtkTable * table, GList * element_list)
+{
+	resize_table (table, widget->cur_num_cols, element_list);
+	relayout_table (table, element_list);
+}
+
+static void
+relayout_tables (AppResizer * widget, gint num_cols)
+{
+	GtkTable *table;
+	GList *table_list, *launcher_list;
+
+	for (table_list = widget->cached_tables_list; table_list != NULL;
+		table_list = g_list_next (table_list))
+	{
+		table = GTK_TABLE (table_list->data);
+		launcher_list = gtk_container_get_children (GTK_CONTAINER (table));
+		launcher_list = g_list_reverse (launcher_list);	/* Fixme - ugly hack because table stores prepend */
+		resize_table (table, num_cols, launcher_list);
+		relayout_table (table, launcher_list);
+		g_list_free (launcher_list);
+	}
+}
+
+static gint
+calculate_num_cols (AppResizer * resizer, gint avail_width)
+{
+	if (resizer->table_elements_homogeneous)
+	{
+		gint num_cols;
+
+		if (resizer->cached_element_width == -1)
+		{
+			GtkTable *table = GTK_TABLE (resizer->cached_tables_list->data);
+			GList *children = gtk_container_get_children (GTK_CONTAINER (table));
+			GtkWidget *table_element = GTK_WIDGET (children->data);
+			g_list_free (children);
+
+			resizer->cached_element_width = table_element->allocation.width;
+			resizer->cached_table_spacing = gtk_table_get_default_col_spacing (table);
+		}
+
+		num_cols =
+			(avail_width +
+			resizer->cached_table_spacing) / (resizer->cached_element_width +
+			resizer->cached_table_spacing);
+		return num_cols;
+	}
+	else
+		g_assert_not_reached ();	/* Fixme - implement... */
+}
+
+static gint
+relayout_tables_if_needed (AppResizer * widget, gint avail_width, gint current_num_cols)
+{
+	gint num_cols = calculate_num_cols (widget, avail_width);
+	if (num_cols < 1)
+	{
+		num_cols = 1;	/* just horiz scroll if avail_width is less than one column */
+	}
+
+	if (current_num_cols != num_cols)
+	{
+		relayout_tables (widget, num_cols);
+		current_num_cols = num_cols;
+	}
+	return current_num_cols;
+}
+
+void
+app_resizer_set_table_cache (AppResizer * widget, GList * cache_list)
+{
+	widget->cached_tables_list = cache_list;
+}
+
+void
+app_resizer_set_homogeneous (AppResizer * widget, gboolean homogeneous)
+{
+	widget->table_elements_homogeneous = homogeneous;
+}
+
+static void
+app_resizer_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
+{
+	/* printf("ENTER - app_resizer_size_allocate\n"); */
+	AppResizer *resizer = APP_RESIZER (widget);
+	GtkWidget *child = GTK_WIDGET (APP_RESIZER (resizer)->child);
+
+	static gboolean first_time = TRUE;
+	gint new_num_cols;
+	gint useable_area;
+
+	if (first_time)
+	{
+		/* we are letting the first show be the "natural" size of the child widget so do nothing. */
+		if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+			(*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+		first_time = FALSE;
+		gtk_layout_set_size (GTK_LAYOUT (resizer), child->allocation.width,
+			child->allocation.height);
+		return;
+	}
+
+	if (!resizer->cached_tables_list)	/* if everthing is currently filtered out - just return */
+	{
+		GtkAllocation child_allocation;
+
+		if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+			(*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+		/* We want the message to center itself and only scroll if it's bigger than the available real size. */
+		child_allocation.x = 0;
+		child_allocation.y = 0;
+		child_allocation.width = MAX (allocation->width, child->requisition.width);
+		child_allocation.height = MAX (allocation->height, child->requisition.height);
+
+		gtk_widget_size_allocate (child, &child_allocation);
+		gtk_layout_set_size (GTK_LAYOUT (resizer), child_allocation.width,
+			child_allocation.height);
+		return;
+	}
+	useable_area =
+		allocation->width - (child->requisition.width -
+		GTK_WIDGET (resizer->cached_tables_list->data)->requisition.width);
+	new_num_cols =
+		relayout_tables_if_needed (APP_RESIZER (resizer), useable_area,
+		resizer->cur_num_cols);
+	if (resizer->cur_num_cols != new_num_cols)
+	{
+		GtkRequisition req;
+
+		/* Have to do this so that it requests, and thus gets allocated, new amount */
+		gtk_widget_size_request (child, &req);
+
+		resizer->cur_num_cols = new_num_cols;
+	}
+
+	if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+		(*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+	gtk_layout_set_size (GTK_LAYOUT (resizer), child->allocation.width,
+		child->allocation.height);
+}
+
+GtkWidget *
+app_resizer_new (GtkVBox * child, gint initial_num_columns, gboolean homogeneous,
+	AppShellData * app_data)
+{
+	AppResizer *widget;
+
+	g_assert (child != NULL);
+	g_assert (GTK_IS_VBOX (child));
+
+	widget = g_object_new (APP_RESIZER_TYPE, NULL);
+	widget->cached_element_width = -1;
+	widget->cur_num_cols = initial_num_columns;
+	widget->table_elements_homogeneous = homogeneous;
+	widget->setting_style = FALSE;
+	widget->app_data = app_data;
+
+	g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (app_resizer_paint_window),
+		app_data);
+
+	gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (child));
+	widget->child = child;
+
+	return GTK_WIDGET (widget);
+}
+
+static void
+app_resizer_destroy (GtkObject * obj)
+{
+}
+
+void
+app_resizer_set_vadjustment_value (GtkWidget * widget, gdouble value)
+{
+	GtkAdjustment *adjust = gtk_layout_get_vadjustment (GTK_LAYOUT (widget));
+	if (value > adjust->upper - adjust->page_size)
+	{
+		value = adjust->upper - adjust->page_size;
+	}
+	gtk_adjustment_set_value (adjust, value);
+}
+
+static gboolean
+app_resizer_paint_window (GtkWidget * widget, GdkEventExpose * event, AppShellData * app_data)
+{
+	/*
+	printf("ENTER - app_resizer_paint_window\n");
+	printf("Area:      %d, %d, %d, %d\n", event->area.x, event->area.y, event->area.width, event->area.height);
+	printf("Allocation:%d, %d, %d, %d\n\n", widget->allocation.x, widget->allocation.y, widget->allocation.width, widget->allocation.height);
+	*/
+
+	gdk_draw_rectangle (GTK_LAYOUT (widget)->bin_window,
+		widget->style->base_gc[GTK_STATE_NORMAL], TRUE, event->area.x, event->area.y,
+		event->area.width, event->area.height);
+
+	if (app_data->selected_group)
+	{
+		GtkWidget *selected_widget = GTK_WIDGET (app_data->selected_group);
+		gdk_draw_rectangle (selected_widget->window,	/* drawing on child window and child coordinates */
+			selected_widget->style->light_gc[GTK_STATE_SELECTED], TRUE,
+			selected_widget->allocation.x, selected_widget->allocation.y,
+			widget->allocation.width,	/* drawing with our coordinates here to draw all the way to the edge. */
+			selected_widget->allocation.height);
+	}
+
+	return FALSE;
+}
diff --git a/libslab/app-resizer.h b/libslab/app-resizer.h
new file mode 100644
index 0000000..b84cfb9
--- /dev/null
+++ b/libslab/app-resizer.h
@@ -0,0 +1,70 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __APP_RESIZER_H__
+#define __APP_RESIZER_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define INITIAL_NUM_COLS 3
+#define APP_RESIZER_TYPE            (app_resizer_get_type ())
+#define APP_RESIZER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_RESIZER_TYPE, AppResizer))
+#define APP_RESIZER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), APP_RESIZER_TYPE, AppResizerClass))
+#define IS_APP_RESIZER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_RESIZER_TYPE))
+#define IS_APP_RESIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_RESIZER_TYPE))
+#define APP_RESIZER_GET_CLASS(obj)  (G_TYPE_CHECK_GET_CLASS ((obj), APP_RESIZER_TYPE, AppResizerClass))
+
+typedef struct _AppResizer AppResizer;
+typedef struct _AppResizerClass AppResizerClass;
+
+struct _AppResizer
+{
+	GtkLayout parent;
+
+	GtkVBox *child;
+	GList *cached_tables_list;
+	gint cached_element_width;
+	gint cached_table_spacing;
+	gboolean table_elements_homogeneous;
+	gint cur_num_cols;
+	gboolean setting_style;
+	AppShellData *app_data;
+};
+
+struct _AppResizerClass
+{
+	GtkLayoutClass parent_class;
+};
+
+void app_resizer_set_homogeneous (AppResizer * widget, gboolean value);
+void remove_container_entries (GtkContainer * widget);
+
+GType app_resizer_get_type (void);
+GtkWidget *app_resizer_new (GtkVBox * child, gint initial_num_columns, gboolean homogeneous,
+	AppShellData * app_data);
+void app_resizer_set_table_cache (AppResizer * widget, GList * cache_list);
+void app_resizer_layout_table_default (AppResizer * widget, GtkTable * table, GList * element_list);
+void app_resizer_set_vadjustment_value (GtkWidget * widget, gdouble value);
+
+G_END_DECLS
+#endif /* __APP_RESIZER_H__ */
diff --git a/libslab/app-shell-startup.c b/libslab/app-shell-startup.c
new file mode 100644
index 0000000..e0491f1
--- /dev/null
+++ b/libslab/app-shell-startup.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <libgnome/gnome-desktop-item.h>
+
+#include "app-shell-startup.h"
+#include "app-shell.h"
+#include "search-bar.h"
+#include "slab-section.h"
+
+gint
+apss_new_instance_cb (BonoboApplication * app, gint argc, char *argv[], gpointer data)
+{
+	AppShellData *app_data = (AppShellData *) data;
+	SlabSection *section = SLAB_SECTION (app_data->filter_section);
+	NldSearchBar *search_bar;
+	gboolean visible;
+
+	/* g_message ("new_instance_cb got called\n"); */
+
+	/* Make sure our implementation has not changed */
+	g_assert (NLD_IS_SEARCH_BAR (section->contents));
+	search_bar = NLD_SEARCH_BAR (section->contents);
+
+	g_object_get (app_data->main_gnome_app, "visible", &visible, NULL);
+	if (!visible)
+	{
+		show_shell (app_data);
+	}
+
+	if (argc)		/* if we are passed a valid startup time */
+	{
+		gchar **results = g_strsplit (argv[0], "_TIME", 0);
+		gint lastentry = 0;
+		guint32 timestamp;
+
+		while (results[lastentry] != NULL)
+			lastentry++;
+		timestamp = (guint32) g_strtod (results[lastentry - 1], NULL);
+		g_strfreev (results);
+
+		/* gdk_x11_window_move_to_current_desktop(window);  */
+		gdk_x11_window_set_user_time (app_data->main_gnome_app->window, timestamp);
+	}
+
+	gtk_window_present (GTK_WINDOW (app_data->main_gnome_app));
+	gtk_widget_grab_focus (GTK_WIDGET (search_bar));
+
+	return argc;
+}
+
+gboolean
+apss_already_running (int argc, char *argv[], BonoboApplication ** app,
+	const gchar * name, gchar * startup_id)
+{
+	gchar const *envp[] = { "LANG", NULL };
+	char * display_name;
+	gchar *serverinfo;
+	BonoboAppClient *client;
+	Bonobo_RegistrationResult reg_res;
+
+	if (bonobo_init (&argc, argv) == FALSE)
+		g_error ("Problem with bonobo_init");
+	if (!bonobo_activate ())
+		g_error ("Problem with bonobo_activate()");
+
+	display_name = (char *) gdk_display_get_name (gdk_display_get_default ());
+	bonobo_activation_set_activation_env_value ("DISPLAY", display_name);
+ 
+	//make this a singleton per display per user
+	display_name = g_strconcat (name, display_name, NULL);
+	*app = bonobo_application_new (display_name);
+	g_free (display_name);
+
+	serverinfo = bonobo_application_create_serverinfo (*app, envp);
+	reg_res = bonobo_application_register_unique (*app, serverinfo, &client);
+	g_free (serverinfo);
+
+	switch (reg_res)
+	{
+	case Bonobo_ACTIVATION_REG_ALREADY_ACTIVE:
+	{
+		gchar *newargv[1];
+		int i;
+		
+		bonobo_object_unref (BONOBO_OBJECT (*app));
+		*app = NULL;
+		
+		newargv[0] = startup_id;
+		i = bonobo_app_client_new_instance (client,
+			((newargv[0] && newargv[0][0] != '\0') ? 1 : 0), newargv, NULL);
+		g_object_unref (client);
+		return TRUE;
+	}
+	case Bonobo_ACTIVATION_REG_SUCCESS:
+		return FALSE;
+		break;
+
+	case Bonobo_ACTIVATION_REG_ERROR:
+	default:
+		g_error ("bonobo activation error when registering unique application");
+		return FALSE;
+	}
+}
diff --git a/libslab/app-shell-startup.h b/libslab/app-shell-startup.h
new file mode 100644
index 0000000..a5ccab1
--- /dev/null
+++ b/libslab/app-shell-startup.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __APP_SHELL_STARTUP_H__
+#define __APP_SHELL_STARTUP_H__
+
+#include <bonobo/bonobo-application.h>
+#include <bonobo/bonobo-main.h>
+
+#define DESKTOP_STARTUP_ID "DESKTOP_STARTUP_ID"
+
+G_BEGIN_DECLS
+
+gint apss_new_instance_cb (BonoboApplication * app, gint argc, char *argv[],
+	gpointer data);
+gboolean apss_already_running (int argc, char *argv[], BonoboApplication ** app,
+	const gchar * name, char * startup_id);
+
+G_END_DECLS
+#endif /* __APP_SHELL_STARTUP_H__ */
diff --git a/libslab/app-shell.c b/libslab/app-shell.c
new file mode 100644
index 0000000..05957e7
--- /dev/null
+++ b/libslab/app-shell.c
@@ -0,0 +1,1407 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomeui/libgnomeui.h>
+#include <gio/gio.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "app-shell.h"
+#include "shell-window.h"
+#include "app-resizer.h"
+#include "slab-section.h"
+#include "slab-gnome-util.h"
+#include "search-bar.h"
+
+#include "application-tile.h"
+#include "themed-icon.h"
+
+#define TILE_EXEC_NAME "Tile_desktop_exec_name"
+#define SECONDS_IN_DAY 86400
+#define EXIT_SHELL_ON_ACTION_START "exit_shell_on_action_start"
+#define EXIT_SHELL_ON_ACTION_HELP "exit_shell_on_action_help"
+#define EXIT_SHELL_ON_ACTION_ADD_REMOVE "exit_shell_on_action_add_remove"
+#define EXIT_SHELL_ON_ACTION_UPGRADE_UNINSTALL "exit_shell_on_action_upgrade_uninstall"
+#define NEW_APPS_FILE_KEY "new_apps_file_key"
+
+static void create_application_category_sections (AppShellData * app_data);
+static GtkWidget *create_filter_section (AppShellData * app_data, const gchar * title);
+static GtkWidget *create_groups_section (AppShellData * app_data, const gchar * title);
+static GtkWidget *create_actions_section (AppShellData * app_data, const gchar * title,
+	void (*actions_handler) (Tile *, TileEvent *, gpointer));
+
+static void generate_category (const char * category, GMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive);
+static void generate_launchers (GMenuTreeDirectory * root_dir, AppShellData * app_data,
+	CategoryData * cat_data, gboolean recursive);
+static void generate_new_apps (AppShellData * app_data);
+static void insert_launcher_into_category (CategoryData * cat_data, GnomeDesktopItem * desktop_item,
+	AppShellData * app_data);
+
+static gboolean main_keypress_callback (GtkWidget * widget, GdkEventKey * event,
+	AppShellData * app_data);
+static gboolean main_delete_callback (GtkWidget * widget, GdkEvent * event,
+	AppShellData * app_data);
+static void application_launcher_clear_search_bar (AppShellData * app_data);
+static void launch_selected_app (AppShellData * app_data);
+static void generate_potential_apps (gpointer catdata, gpointer user_data);
+
+static void relayout_shell (AppShellData * app_data);
+static gboolean handle_filter_changed (NldSearchBar * search_bar, int context, const char *text,
+	gpointer user_data);
+static void handle_group_clicked (Tile * tile, TileEvent * event, gpointer user_data);
+static void set_state (AppShellData * app_data, GtkWidget * widget);
+static void populate_groups_section (AppShellData * app_data);
+static void generate_filtered_lists (gpointer catdata, gpointer user_data);
+static void show_no_results_message (AppShellData * app_data, GtkWidget * containing_vbox);
+static void populate_application_category_sections (AppShellData * app_data,
+	GtkWidget * containing_vbox);
+static void populate_application_category_section (AppShellData * app_data, SlabSection * section,
+	GList * launcher_list);
+static void tile_activated_cb (Tile * tile, TileEvent * event, gpointer user_data);
+static void handle_launcher_single_clicked (Tile * launcher, gpointer data);
+static void handle_menu_action_performed (Tile * launcher, TileEvent * event, TileAction * action,
+	gpointer data);
+static gint application_launcher_compare (gconstpointer a, gconstpointer b);
+static void gmenu_tree_changed_callback (GMenuTree * tree, gpointer user_data);
+gboolean regenerate_categories (AppShellData * app_data);
+
+void
+hide_shell (AppShellData * app_data)
+{
+	gtk_window_get_position (GTK_WINDOW (app_data->main_gnome_app),
+		&app_data->main_gnome_app_window_x, &app_data->main_gnome_app_window_y);
+	/* printf("x:%d, y:%d\n", app_data->main_gnome_app_window_x, app_data->main_gnome_app_window_y); */
+	/* clear the search bar now so reshowing is fast and flicker free - BNC#283186 */
+	application_launcher_clear_search_bar (app_data);
+	gtk_widget_hide (app_data->main_gnome_app);
+}
+
+void
+show_shell (AppShellData * app_data)
+{
+	gtk_widget_show_all (app_data->main_gnome_app);
+	if (!app_data->static_actions)
+		gtk_widget_hide_all (app_data->actions_section);  /* don't show unless a launcher is selected */
+
+	if (app_data->main_gnome_app_window_shown_once)
+		gtk_window_move (GTK_WINDOW (app_data->main_gnome_app),
+			app_data->main_gnome_app_window_x, app_data->main_gnome_app_window_y);
+
+	/* if this is the first time shown, need to clear this handler */
+	else
+		shell_window_clear_resize_handler (SHELL_WINDOW (app_data->shell));
+	app_data->main_gnome_app_window_shown_once = TRUE;
+}
+
+gboolean
+create_main_window (AppShellData * app_data, const gchar * app_name, const gchar * title,
+	const gchar * window_icon, gint width, gint height, gboolean hidden)
+{
+	GtkWidget *main_app = gnome_app_new (app_name, title);
+	app_data->main_gnome_app = main_app;
+	gtk_widget_set_name (main_app, app_name);
+	/* gtk_window_set_default_size(GTK_WINDOW(main_app), width, height); */
+	gtk_window_set_icon_name (GTK_WINDOW (main_app), window_icon);
+	gnome_app_set_contents (GNOME_APP (main_app), app_data->shell);
+
+	g_signal_connect (main_app, "delete-event", G_CALLBACK (main_delete_callback), app_data);
+	g_signal_connect (main_app, "key-press-event", G_CALLBACK (main_keypress_callback),
+		app_data);
+
+	gtk_window_set_position (GTK_WINDOW (app_data->main_gnome_app), GTK_WIN_POS_CENTER);
+	if (!hidden)
+		show_shell (app_data);
+	gtk_main ();
+
+	return TRUE;
+}
+
+static void
+generate_potential_apps (gpointer catdata, gpointer user_data)
+{
+	GHashTable *app_hash = (GHashTable *) user_data;
+	CategoryData *data = (CategoryData *) catdata;
+	gchar *uri;
+
+	GList *launcher_list = data->filtered_launcher_list;
+
+	while (launcher_list)
+	{
+		g_object_get (launcher_list->data, "tile-uri", &uri, NULL);
+		/* eliminate dups of same app in multiple categories */
+		if (!g_hash_table_lookup (app_hash, uri))
+			g_hash_table_insert (app_hash, uri, launcher_list->data);
+		else
+			g_free (uri);
+		launcher_list = g_list_next (launcher_list);
+	}
+}
+
+static gboolean
+return_first_entry (gpointer key, gpointer value, gpointer unused)
+{
+	return TRUE;	/*better way to pull an entry out ? */
+}
+
+static void
+launch_selected_app (AppShellData * app_data)
+{
+	GHashTable *app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	guint num_apps;
+
+	g_list_foreach (app_data->categories_list, generate_potential_apps, app_hash);
+	num_apps = g_hash_table_size (app_hash);
+	if (num_apps == 1)
+	{
+		ApplicationTile *launcher =
+			APPLICATION_TILE (g_hash_table_find (app_hash, return_first_entry, NULL));
+		g_hash_table_destroy (app_hash);
+		handle_launcher_single_clicked (TILE (launcher), app_data);
+		return;
+	}
+
+	g_hash_table_destroy (app_hash);
+}
+
+static gboolean
+main_keypress_callback (GtkWidget * widget, GdkEventKey * event, AppShellData * app_data)
+{
+	if (event->keyval == GDK_Return)
+	{
+		SlabSection *section = SLAB_SECTION (app_data->filter_section);
+		NldSearchBar *search_bar;
+
+		/* Make sure our implementation has not changed */
+		g_assert (NLD_IS_SEARCH_BAR (section->contents));
+		search_bar = NLD_SEARCH_BAR (section->contents);
+		if (nld_search_bar_has_focus (search_bar))
+		{
+			launch_selected_app (app_data);
+			return TRUE;
+		}
+	}
+
+	/* quit on ESC or Ctl-W or Ctl-Q */
+	if (event->keyval == GDK_Escape ||
+		((event->keyval == GDK_w || event->keyval == GDK_W)	&& (event->state & GDK_CONTROL_MASK)) ||
+		((event->keyval == GDK_q || event->keyval == GDK_Q) && (event->state & GDK_CONTROL_MASK)))
+	{
+		if (app_data->exit_on_close)
+			gtk_main_quit ();
+		else
+			hide_shell (app_data);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+main_delete_callback (GtkWidget * widget, GdkEvent * event, AppShellData * app_data)
+{
+	if (app_data->exit_on_close)
+	{
+		gtk_main_quit ();
+		return FALSE;
+	}
+
+	hide_shell (app_data);
+	return TRUE;		/* stop the processing of this event */
+}
+
+void
+layout_shell (AppShellData * app_data, const gchar * filter_title, const gchar * groups_title,
+	const gchar * actions_title, GSList * actions,
+	void (*actions_handler) (Tile *, TileEvent *, gpointer))
+{
+	GtkWidget *filter_section;
+	GtkWidget *groups_section;
+	GtkWidget *actions_section;
+
+	GtkWidget *left_vbox;
+	GtkWidget *right_vbox;
+	gint num_cols;
+
+	GtkWidget *sw;
+	GtkAdjustment *adjustment;
+
+	app_data->shell = shell_window_new (app_data);
+	app_data->static_actions = actions;
+
+	right_vbox = gtk_vbox_new (FALSE, CATEGORY_SPACING);
+
+	num_cols = SIZING_SCREEN_WIDTH_LARGE_NUMCOLS;
+	if (gdk_screen_width () <= SIZING_SCREEN_WIDTH_LARGE)
+	{
+		if (gdk_screen_width () <= SIZING_SCREEN_WIDTH_MEDIUM)
+			num_cols = SIZING_SCREEN_WIDTH_SMALL_NUMCOLS;
+		else
+			num_cols = SIZING_SCREEN_WIDTH_MEDIUM_NUMCOLS;
+	}
+	app_data->category_layout =
+		app_resizer_new (GTK_VBOX (right_vbox), num_cols, TRUE, app_data);
+
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+		GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+	gtk_container_add (GTK_CONTAINER (sw), app_data->category_layout);
+	adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
+	g_object_set (adjustment, "step-increment", (double) 20, NULL);
+
+	create_application_category_sections (app_data);
+	populate_application_category_sections (app_data, right_vbox);
+	app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
+		app_data->cached_tables_list);
+
+	gtk_container_set_focus_vadjustment (GTK_CONTAINER (right_vbox),
+		gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw)));
+
+	left_vbox = gtk_vbox_new (FALSE, 15);
+
+	filter_section = create_filter_section (app_data, filter_title);
+	app_data->filter_section = filter_section;
+	gtk_box_pack_start (GTK_BOX (left_vbox), filter_section, FALSE, FALSE, 0);
+
+	groups_section = create_groups_section (app_data, groups_title);
+	app_data->groups_section = groups_section;
+	populate_groups_section (app_data);
+	gtk_box_pack_start (GTK_BOX (left_vbox), groups_section, FALSE, FALSE, 0);
+
+	actions_section = create_actions_section (app_data, actions_title, actions_handler);
+	app_data->actions_section = actions_section;
+	gtk_box_pack_start (GTK_BOX (left_vbox), actions_section, FALSE, FALSE, 0);
+
+	shell_window_set_contents (SHELL_WINDOW (app_data->shell), left_vbox, sw);
+}
+
+static gboolean
+relayout_shell_partial (gpointer user_data)
+{
+	AppShellData *app_data = (AppShellData *) user_data;
+	GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
+	CategoryData *data;
+
+	if (app_data->stop_incremental_relayout)
+		return FALSE;
+
+	if (app_data->incremental_relayout_cat_list != NULL)
+	{
+		/* There are still categories to layout */
+		data = (CategoryData *) app_data->incremental_relayout_cat_list->data;
+		if (data->filtered_launcher_list != NULL)
+		{
+			populate_application_category_section (app_data, data->section,
+				data->filtered_launcher_list);
+			gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (data->section), TRUE, TRUE,
+				0);
+			app_data->filtered_out_everything = FALSE;
+		}
+
+		app_data->incremental_relayout_cat_list =
+			g_list_next (app_data->incremental_relayout_cat_list);
+		return TRUE;
+	}
+
+	/* We're done laying out the categories; finish up */
+	if (app_data->filtered_out_everything)
+		show_no_results_message (app_data, GTK_WIDGET (vbox));
+
+	app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
+		app_data->cached_tables_list);
+	populate_groups_section (app_data);
+
+	gtk_widget_show_all (app_data->category_layout);
+	gdk_window_set_cursor (app_data->shell->window, NULL);
+
+	app_data->stop_incremental_relayout = TRUE;
+	return FALSE;
+}
+
+static void
+relayout_shell_incremental (AppShellData * app_data)
+{
+	GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
+
+	app_data->stop_incremental_relayout = FALSE;
+	app_data->filtered_out_everything = TRUE;
+	app_data->incremental_relayout_cat_list = app_data->categories_list;
+
+	if (app_data->cached_tables_list)
+		g_list_free (app_data->cached_tables_list);
+	app_data->cached_tables_list = NULL;
+
+	remove_container_entries (GTK_CONTAINER (vbox));
+
+	g_idle_add ((GSourceFunc) relayout_shell_partial, app_data);
+}
+
+static void
+relayout_shell (AppShellData * app_data)
+{
+	GtkWidget *shell = app_data->shell;
+	GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
+
+	populate_application_category_sections (app_data, GTK_WIDGET (vbox));
+	app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
+		app_data->cached_tables_list);
+	populate_groups_section (app_data);
+
+	gtk_widget_show_all (shell);
+	if (!app_data->static_actions && !app_data->last_clicked_launcher)
+		gtk_widget_hide_all (app_data->actions_section);  /* don't show unless a launcher is selected */
+}
+
+static GtkWidget *
+create_actions_section (AppShellData * app_data, const gchar * title,
+	void (*actions_handler) (Tile *, TileEvent *, gpointer))
+{
+	GtkWidget *section, *launcher;
+	GtkWidget *vbox;
+	GSList *actions;
+	AppAction *action;
+	AtkObject *a11y_cat;
+
+	g_assert (app_data != NULL);
+
+	section = slab_section_new (title, Style1);
+	g_object_ref (section);
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	slab_section_set_contents (SLAB_SECTION (section), vbox);
+
+	if (app_data->static_actions)
+	{
+		for (actions = app_data->static_actions; actions; actions = actions->next)
+		{
+			GtkWidget *header;
+
+			action = (AppAction *) actions->data;
+			header = gtk_label_new (action->name);
+			gtk_misc_set_alignment (GTK_MISC (header), 0, 0.5);
+			launcher = nameplate_tile_new (NULL, NULL, header, NULL);
+
+			g_object_set_data (G_OBJECT (launcher), APP_ACTION_KEY, action->item);
+			g_signal_connect (launcher, "tile-activated", G_CALLBACK (actions_handler),
+				app_data);
+			gtk_box_pack_start (GTK_BOX (vbox), launcher, FALSE, FALSE, 0);
+
+			a11y_cat = gtk_widget_get_accessible (GTK_WIDGET (launcher));
+			atk_object_set_name (a11y_cat, action->name);
+		}
+	}
+
+	return section;
+}
+
+static GtkWidget *
+create_groups_section (AppShellData * app_data, const gchar * title)
+{
+	GtkWidget *section;
+	GtkWidget *vbox;
+
+	g_assert (app_data != NULL);
+
+	section = slab_section_new (title, Style1);
+	g_object_ref (section);
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	slab_section_set_contents (SLAB_SECTION (section), vbox);
+
+	return section;
+}
+
+static void
+populate_groups_section (AppShellData * app_data)
+{
+	SlabSection *section = SLAB_SECTION (app_data->groups_section);
+	GtkVBox *vbox;
+	GList *cat_list;
+
+	/* Make sure our implementation has not changed and it's still a GtkVBox */
+	g_assert (GTK_IS_VBOX (section->contents));
+
+	vbox = GTK_VBOX (section->contents);
+	remove_container_entries (GTK_CONTAINER (vbox));
+
+	cat_list = app_data->categories_list;
+	do
+	{
+		CategoryData *data = (CategoryData *) cat_list->data;
+		if (NULL != data->filtered_launcher_list)
+		{
+			gtk_widget_set_state (GTK_WIDGET (data->group_launcher), GTK_STATE_NORMAL);
+			gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (data->group_launcher),
+				FALSE, FALSE, 0);
+		}
+	}
+	while (NULL != (cat_list = g_list_next (cat_list)));
+}
+
+static void
+handle_group_clicked (Tile * tile, TileEvent * event, gpointer user_data)
+{
+	AppShellData *app_data = (AppShellData *) user_data;
+	GtkWidget *section = NULL;
+
+	gint clicked_pos =
+		GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tile), GROUP_POSITION_NUMBER_KEY));
+
+	GList *cat_list = app_data->categories_list;
+
+	gint total = 0;
+	do
+	{
+		CategoryData *cat_data = (CategoryData *) cat_list->data;
+		gint pos =
+			GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cat_data->group_launcher),
+				GROUP_POSITION_NUMBER_KEY));
+		if (pos == clicked_pos)
+		{
+			section = GTK_WIDGET (cat_data->section);
+			break;
+		}
+
+		if (NULL != cat_data->filtered_launcher_list)
+		{
+			total += GTK_WIDGET (cat_data->section)->allocation.height +
+				CATEGORY_SPACING;
+		}
+	}
+	while (NULL != (cat_list = g_list_next (cat_list)));
+
+	g_assert (section != NULL);
+	set_state (app_data, section);
+
+	app_resizer_set_vadjustment_value (app_data->category_layout, total);
+}
+
+static void
+set_state (AppShellData * app_data, GtkWidget * widget)
+{
+	if (app_data->selected_group)
+	{
+		slab_section_set_selected (app_data->selected_group, FALSE);
+		app_data->selected_group = NULL;
+	}
+
+	if (widget)
+	{
+		app_data->selected_group = SLAB_SECTION (widget);
+		slab_section_set_selected (SLAB_SECTION (widget), TRUE);
+	}
+	gtk_widget_queue_draw (app_data->shell);
+}
+
+static GtkWidget *
+create_filter_section (AppShellData * app_data, const gchar * title)
+{
+	GtkWidget *section;
+
+	GtkWidget *search_bar;
+
+	section = slab_section_new (title, Style1);
+	g_object_ref (section);
+
+	search_bar = nld_search_bar_new ();
+	nld_search_bar_set_search_timeout (NLD_SEARCH_BAR (search_bar), 0);
+	slab_section_set_contents (SLAB_SECTION (section), search_bar);
+
+	g_signal_connect (G_OBJECT (search_bar), "search", G_CALLBACK (handle_filter_changed),
+		app_data);
+
+	return section;
+}
+
+static gboolean
+handle_filter_changed_delayed (gpointer user_data)
+{
+	AppShellData *app_data = (AppShellData *) user_data;
+
+	g_list_foreach (app_data->categories_list, generate_filtered_lists,
+		(gpointer) app_data->filter_string);
+	app_data->last_clicked_launcher = NULL;
+
+	/*  showing the updates incremtally is very visually distracting. Much worse than just blanking until
+	   the incremental work is done and then doing one show. It would be nice to optimize this though
+	   somehow and not even show any change but the cursor change until all the work is done. But since
+	   we do the work incrementally in an idle loop I don't know how else besides hiding to not show
+	   incremental updates
+	 */
+	/* gdk_window_freeze_updates(app_data->category_layout->window); */
+	gtk_widget_hide (app_data->category_layout);
+	app_data->busy_cursor =
+		gdk_cursor_new_for_display (gtk_widget_get_display (app_data->shell), GDK_WATCH);
+	gdk_window_set_cursor (app_data->shell->window, app_data->busy_cursor);
+	gdk_cursor_unref (app_data->busy_cursor);
+
+	set_state (app_data, NULL);
+	app_resizer_set_vadjustment_value (app_data->category_layout, 0);
+
+	relayout_shell_incremental (app_data);
+
+	app_data->filter_changed_timeout = 0;
+	return FALSE;
+}
+
+static gboolean
+handle_filter_changed (NldSearchBar * search_bar, int context, const char *text, gpointer data)
+{
+	AppShellData *app_data;
+
+	app_data = (AppShellData *) data;
+
+	if (app_data->filter_string)
+		g_free (app_data->filter_string);
+	app_data->filter_string = g_strdup (text);
+
+	if (app_data->filter_changed_timeout)
+		g_source_remove (app_data->filter_changed_timeout);
+
+	app_data->filter_changed_timeout =
+		g_timeout_add (75, handle_filter_changed_delayed, app_data);
+	app_data->stop_incremental_relayout = TRUE;
+
+	return FALSE;
+}
+
+static void
+generate_filtered_lists (gpointer catdata, gpointer user_data)
+{
+	CategoryData *data = (CategoryData *) catdata;
+
+	/* Fixme - everywhere you use ascii you need to fix up for multibyte */
+	gchar *filter_string = g_ascii_strdown (user_data, -1);
+	gchar *temp1, *temp2;
+	GList *launcher_list = data->launcher_list;
+
+	g_list_free (data->filtered_launcher_list);
+	data->filtered_launcher_list = NULL;
+
+	do
+	{
+		ApplicationTile *launcher = APPLICATION_TILE (launcher_list->data);
+		const gchar *filename;
+
+		temp1 = NULL;
+		temp2 = NULL;
+
+		/* Since the filter may remove this entry from the
+		   container it will not get a mouse out event */
+		gtk_widget_set_state (GTK_WIDGET (launcher), GTK_STATE_NORMAL);
+		filename = g_object_get_data (G_OBJECT (launcher), TILE_EXEC_NAME); /* do I need to free this */
+
+		temp1 = g_ascii_strdown (launcher->name, -1);
+		if (launcher->description)
+			temp2 = g_ascii_strdown (launcher->description, -1);
+		if (g_strrstr (temp1, filter_string) || (launcher->description
+				&& g_strrstr (temp2, filter_string))
+			|| g_strrstr (filename, filter_string))
+		{
+			data->filtered_launcher_list =
+				g_list_append (data->filtered_launcher_list, launcher);
+		}
+		if (temp1)
+			g_free (temp1);
+		if (temp2)
+			g_free (temp2);
+	}
+	while (NULL != (launcher_list = g_list_next (launcher_list)));
+	g_free (filter_string);
+}
+
+static void
+delete_old_data (AppShellData * app_data)
+{
+	GList *temp;
+	GList *cat_list;
+
+	g_assert (app_data != NULL);
+	g_assert (app_data->categories_list != NULL);
+
+	cat_list = app_data->categories_list;
+
+	do
+	{
+		CategoryData *data = (CategoryData *) cat_list->data;
+		gtk_widget_destroy (GTK_WIDGET (data->section));
+		gtk_widget_destroy (GTK_WIDGET (data->group_launcher));
+		g_object_unref (data->section);
+		g_object_unref (data->group_launcher);
+		g_free (data->category);
+
+		for (temp = data->launcher_list; temp; temp = g_list_next (temp))
+		{
+			g_free (g_object_get_data (G_OBJECT (temp->data), TILE_EXEC_NAME));
+			g_object_unref (temp->data);
+		}
+
+		g_list_free (data->launcher_list);
+		g_list_free (data->filtered_launcher_list);
+		g_free (data);
+	}
+	while (NULL != (cat_list = g_list_next (cat_list)));
+
+	g_list_free (app_data->categories_list);
+	app_data->categories_list = NULL;
+	app_data->selected_group = NULL;
+}
+
+static void
+create_application_category_sections (AppShellData * app_data)
+{
+	GList *cat_list;
+	AtkObject *a11y_cat;
+	gint pos = 0;
+
+	g_assert (app_data != NULL);
+	g_assert (app_data->categories_list != NULL);	/* Fixme - pop up a dialog box and then close */
+
+	cat_list = app_data->categories_list;
+
+	do
+	{
+		CategoryData *data = (CategoryData *) cat_list->data;
+		GtkWidget *header = gtk_label_new (data->category);
+		gchar *markup;
+		GtkWidget *hbox;
+		GtkWidget *table;
+
+		gtk_misc_set_alignment (GTK_MISC (header), 0, 0.5);
+		data->group_launcher = TILE (nameplate_tile_new (NULL, NULL, header, NULL));
+		g_object_ref (data->group_launcher);
+
+		g_object_set_data (G_OBJECT (data->group_launcher), GROUP_POSITION_NUMBER_KEY,
+			GINT_TO_POINTER (pos));
+		pos++;
+		g_signal_connect (data->group_launcher, "tile-activated",
+			G_CALLBACK (handle_group_clicked), app_data);
+		a11y_cat = gtk_widget_get_accessible (GTK_WIDGET (data->group_launcher));
+		atk_object_set_name (a11y_cat, data->category);
+
+		markup = g_markup_printf_escaped ("<span size=\"x-large\" weight=\"bold\">%s</span>",
+			data->category);
+		data->section = SLAB_SECTION (slab_section_new_with_markup (markup, Style2));
+
+		/* as we filter these will be added/removed from parent container and we dont want them destroyed */
+		g_object_ref (data->section);
+		g_free (markup);
+
+		hbox = gtk_hbox_new (FALSE, 0);
+		table = gtk_table_new (0, 0, TRUE);
+		gtk_table_set_col_spacings (GTK_TABLE (table), 5);
+		gtk_table_set_row_spacings (GTK_TABLE (table), 5);
+		gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 15);
+		slab_section_set_contents (SLAB_SECTION (data->section), hbox);
+	}
+	while (NULL != (cat_list = g_list_next (cat_list)));
+}
+
+static void
+show_no_results_message (AppShellData * app_data, GtkWidget * containing_vbox)
+{
+	gchar *markup;
+
+	if (!app_data->filtered_out_everything_widget)
+	{
+		GtkWidget *hbox;
+		GtkWidget *image;
+		GtkWidget *label;
+
+		app_data->filtered_out_everything_widget = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+		g_object_ref (app_data->filtered_out_everything_widget);
+
+		hbox = gtk_hbox_new (FALSE, 0);
+		image = themed_icon_new ("face-surprise", GTK_ICON_SIZE_DIALOG);
+		gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+		label = gtk_label_new (NULL);
+		gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+		gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 15);
+		app_data->filtered_out_everything_widget_label = GTK_LABEL (label);
+
+		gtk_container_add (GTK_CONTAINER (app_data->filtered_out_everything_widget), hbox);
+	}
+
+	markup = g_markup_printf_escaped (
+		_("<span size=\"large\"><b>No matches found.</b> </span><span>\n\n Your filter \"<b>%s</b>\" does not match any items.</span>"),
+		app_data->filter_string);
+	gtk_label_set_text (app_data->filtered_out_everything_widget_label, markup);
+	gtk_label_set_use_markup (app_data->filtered_out_everything_widget_label, TRUE);
+	gtk_box_pack_start (GTK_BOX (containing_vbox), app_data->filtered_out_everything_widget,
+		TRUE, TRUE, 0);
+	g_free (markup);
+}
+
+static void
+populate_application_category_sections (AppShellData * app_data, GtkWidget * containing_vbox)
+{
+	GList *cat_list = app_data->categories_list;
+	gboolean filtered_out_everything = TRUE;
+	if (app_data->cached_tables_list)
+		g_list_free (app_data->cached_tables_list);
+	app_data->cached_tables_list = NULL;
+
+	remove_container_entries (GTK_CONTAINER (containing_vbox));
+	do
+	{
+		CategoryData *data = (CategoryData *) cat_list->data;
+		if (NULL != data->filtered_launcher_list)
+		{
+			populate_application_category_section (app_data, data->section,
+				data->filtered_launcher_list);
+			gtk_box_pack_start (GTK_BOX (containing_vbox), GTK_WIDGET (data->section),
+				TRUE, TRUE, 0);
+			filtered_out_everything = FALSE;
+		}
+	}
+	while (NULL != (cat_list = g_list_next (cat_list)));
+
+	if (TRUE == filtered_out_everything)
+		show_no_results_message (app_data, containing_vbox);
+}
+
+static void
+populate_application_category_section (AppShellData * app_data, SlabSection * section,
+	GList * launcher_list)
+{
+	GtkWidget *hbox;
+	GtkTable *table;
+	GList *children;
+
+	g_assert (GTK_IS_HBOX (section->contents));
+	hbox = GTK_WIDGET (section->contents);
+
+	children = gtk_container_get_children (GTK_CONTAINER (hbox));
+	table = children->data;
+	g_list_free (children);
+
+	/* Make sure our implementation has not changed and it's still a GtkTable */
+	g_assert (GTK_IS_TABLE (table));
+
+	app_data->cached_tables_list = g_list_append (app_data->cached_tables_list, table);
+
+	app_resizer_layout_table_default (APP_RESIZER (app_data->category_layout), table,
+		launcher_list);
+	
+}
+
+gboolean
+regenerate_categories (AppShellData * app_data)
+{
+	delete_old_data (app_data);
+	generate_categories (app_data);
+	create_application_category_sections (app_data);
+	relayout_shell (app_data);
+
+	return FALSE;	/* remove this function from the list */
+}
+
+static void
+gmenu_tree_changed_callback (GMenuTree * old_tree, gpointer user_data)
+{
+	/*
+	This method only gets called on the first change (gmenu appears to ignore subsequent) until
+	we reget the root dir which we can't do in this method because if we do for some reason this
+	method then gets called multiple times for one actual change. This actually is okay because
+	it's probably a good idea to wait a couple seconds to regenerate the categories in case there
+	are multiple quick changes being made, no sense regenerating multiple times.
+	*/
+	g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000, (GSourceFunc) regenerate_categories,
+		user_data, NULL);
+}
+
+AppShellData *
+appshelldata_new (const gchar * menu_name, NewAppConfig * new_apps, const gchar * gconf_keys_prefix,
+	GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close)
+{
+	AppShellData *app_data = g_new0 (AppShellData, 1);
+	app_data->gconf_prefix = gconf_keys_prefix;
+	app_data->new_apps = new_apps;
+	app_data->menu_name = menu_name;
+	app_data->icon_size = icon_size;
+	app_data->stop_incremental_relayout = TRUE;
+	app_data->show_tile_generic_name = show_tile_generic_name;
+	app_data->exit_on_close = exit_on_close;
+	return app_data;
+}
+
+void
+generate_categories (AppShellData * app_data)
+{
+	GMenuTreeDirectory *root_dir;
+	GSList *contents, *l;
+	gboolean need_misc = FALSE;
+	
+	if (!app_data->tree)
+	{
+		app_data->tree = gmenu_tree_lookup (app_data->menu_name, GMENU_TREE_FLAGS_NONE);
+		gmenu_tree_add_monitor (app_data->tree, gmenu_tree_changed_callback, app_data);
+	}
+	root_dir = gmenu_tree_get_root_directory (app_data->tree);
+	if (root_dir)
+		contents = gmenu_tree_directory_get_contents (root_dir);
+	if (!root_dir || !contents)
+	{
+		GtkWidget *dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
+			GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Failure loading - %s",
+			app_data->menu_name);
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		exit (1);	/* Fixme - is there a GNOME/GTK way to do this. */
+	}
+
+	for (l = contents; l; l = l->next)
+	{
+		const char *category;
+		GMenuTreeItem *item = l->data;
+
+		switch (gmenu_tree_item_get_type (item))
+		{
+		case GMENU_TREE_ITEM_DIRECTORY:
+			category = gmenu_tree_directory_get_name ((GMenuTreeDirectory*)item);
+			generate_category(category, (GMenuTreeDirectory*)item, app_data, TRUE);
+			break;
+		case GMENU_TREE_ITEM_ENTRY:
+			need_misc = TRUE;
+			break;
+		default:
+			break;
+		}
+
+		gmenu_tree_item_unref (item);
+	}
+	g_slist_free (contents);
+
+	if (need_misc)
+		generate_category (_("Other"), root_dir, app_data, FALSE);
+
+	if (app_data->hash)
+	{
+		g_hash_table_destroy (app_data->hash);
+		app_data->hash = NULL;
+	}
+	
+	gmenu_tree_item_unref (root_dir);
+
+	if (app_data->new_apps && (app_data->new_apps->max_items > 0))
+		generate_new_apps (app_data);
+}
+
+static void
+generate_category (const char * category, GMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive)
+{
+	CategoryData *data;
+	/* This is not needed. GMenu already returns an ordered, non duplicate list
+	GList *list_entry;
+	list_entry =
+		g_list_find_custom (app_data->categories_list, category,
+		category_name_compare);
+	if (!list_entry)
+	{
+	*/
+		data = g_new0 (CategoryData, 1);
+		data->category = g_strdup (category);
+		app_data->categories_list =
+			/* use the gmenu order instead of alphabetical */
+			g_list_append (app_data->categories_list, data);
+			/* g_list_insert_sorted (app_data->categories_list, data, category_data_compare); */
+	/*
+	}
+	else
+	{
+		data = list_entry->data;
+	}
+	*/
+
+	if (app_data->hash)	/* used to eliminate dups on a per category basis. */
+		g_hash_table_destroy (app_data->hash);
+	app_data->hash = g_hash_table_new (g_str_hash, g_str_equal);
+	generate_launchers (root_dir, app_data, data, recursive);
+}
+
+static gboolean
+check_specific_apps_hack (GnomeDesktopItem * item)
+{
+	static const gchar *COMMAND_LINE_LOCKDOWN_GCONF_KEY =
+		"/desktop/gnome/lockdown/disable_command_line";
+	static const gchar *COMMAND_LINE_LOCKDOWN_DESKTOP_CATEGORY = "TerminalEmulator";
+	static gboolean got_lockdown_value = FALSE;
+	static gboolean command_line_lockdown;
+
+	gchar *path;
+	const char *exec;
+
+	if (!got_lockdown_value)
+	{
+		got_lockdown_value = TRUE;
+		command_line_lockdown = get_slab_gconf_bool (COMMAND_LINE_LOCKDOWN_GCONF_KEY);
+	}
+
+	/* This seems like an ugly hack but it's the way it's currently done in the old control center */
+	exec = gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_EXEC);
+
+	/* discard xscreensaver if gnome-screensaver is installed */
+	if ((exec && !strcmp (exec, "xscreensaver-demo"))
+		&& (path = g_find_program_in_path ("gnome-screensaver-preferences")))
+	{
+		g_free (path);
+		return TRUE;
+	}
+
+	/* discard gnome-keyring-manager if CASA is installed */
+	if ((exec && !strcmp (exec, "gnome-keyring-manager"))
+		&& (path = g_find_program_in_path ("CASAManager.sh")))
+	{
+		g_free (path);
+		return TRUE;
+	}
+
+	/* discard terminals if lockdown key is set */
+	if (command_line_lockdown)
+	{
+		const gchar *categories =
+			gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_CATEGORIES);
+		if (g_strrstr (categories, COMMAND_LINE_LOCKDOWN_DESKTOP_CATEGORY))
+		{
+			/* printf ("eliminating %s\n", gnome_desktop_item_get_location (item)); */
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+generate_launchers (GMenuTreeDirectory * root_dir, AppShellData * app_data, CategoryData * cat_data, gboolean recursive)
+{
+	GnomeDesktopItem *desktop_item;
+	const gchar *desktop_file;
+	GSList *contents, *l;
+
+	contents = gmenu_tree_directory_get_contents (root_dir);
+	for (l = contents; l; l = l->next)
+	{
+		switch (gmenu_tree_item_get_type (l->data))
+		{
+		case GMENU_TREE_ITEM_DIRECTORY:
+			/* g_message ("Found sub-category %s", gmenu_tree_directory_get_name (l->data)); */
+			if (recursive)
+				generate_launchers (l->data, app_data, cat_data, TRUE);
+			break;
+		case GMENU_TREE_ITEM_ENTRY:
+			/* g_message ("Found item name is:%s", gmenu_tree_entry_get_name (l->data)); */
+			desktop_file = gmenu_tree_entry_get_desktop_file_path (l->data);
+			if (desktop_file)
+			{
+				if (g_hash_table_lookup (app_data->hash, desktop_file))
+				{
+					break;	/* duplicate */
+				}
+				/* Fixme - make sure it's safe to store this without duping it. As far as I can tell it is
+				   safe as long as I don't hang on to this anylonger than I hang on to the GMenuTreeEntry*
+				   which brings up another point - am I supposed to free these or does freeing the top level recurse
+				*/
+				g_hash_table_insert (app_data->hash, (gpointer) desktop_file,
+					(gpointer) desktop_file);
+			}
+			desktop_item = gnome_desktop_item_new_from_file (desktop_file, 0, NULL);
+			if (!desktop_item)
+			{
+				g_critical ("Failure - gnome_desktop_item_new_from_file(%s)",
+					    desktop_file);
+				break;
+			}
+			if (!check_specific_apps_hack (desktop_item))
+				insert_launcher_into_category (cat_data, desktop_item, app_data);
+			gnome_desktop_item_unref (desktop_item);
+			break;
+		default:
+			break;
+		}
+
+		gmenu_tree_item_unref (l->data);
+	}
+	g_slist_free (contents);
+}
+
+static void
+generate_new_apps (AppShellData * app_data)
+{
+	GHashTable *all_apps_cache = NULL;
+	gchar *all_apps;
+	GError *error = NULL;
+	gchar *separator = "\n";
+	gchar *gconf_key;
+
+	gchar *basename;
+	gchar *all_apps_file_name;
+	gchar **all_apps_split;
+	gint x;
+	gboolean got_new_apps;
+	CategoryData *new_apps_category = NULL;
+	GList *categories, *launchers;
+	GHashTable *new_apps_dups;
+
+	gconf_key = g_strdup_printf ("%s%s", app_data->gconf_prefix, NEW_APPS_FILE_KEY);
+	basename = get_slab_gconf_string (gconf_key);
+	g_free (gconf_key);
+	if (!basename)
+	{
+		g_warning ("Failure getting gconf key NEW_APPS_FILE_KEY");
+		return;
+	}
+
+	all_apps_file_name = g_build_filename (g_get_home_dir (), basename, NULL);
+	g_free (basename);
+
+	if (!g_file_get_contents (all_apps_file_name, &all_apps, NULL, &error))
+	{
+		/* If file does not exist, this is the first time this user has run this, create the baseline file */
+		GList *categories, *launchers;
+		GString *gstr;
+		gchar *dirname;
+
+		g_error_free (error);
+		error = NULL;
+
+		/* best initial size determined by running on a couple different platforms */
+		gstr = g_string_sized_new (10000);
+
+		for (categories = app_data->categories_list; categories; categories = categories->next)
+		{
+			CategoryData *data = categories->data;
+			for (launchers = data->launcher_list; launchers; launchers = launchers->next)
+			{
+				Tile *tile = TILE (launchers->data);
+				GnomeDesktopItem *item =
+					application_tile_get_desktop_item (APPLICATION_TILE (tile));
+				const gchar *uri = gnome_desktop_item_get_location (item);
+				g_string_append (gstr, uri);
+				g_string_append (gstr, separator);
+			}
+		}
+
+		dirname = g_path_get_dirname (all_apps_file_name);
+		g_mkdir_with_parents (dirname, 0700);	/* creates if does not exist */
+		g_free (dirname);
+
+		if (!g_file_set_contents (all_apps_file_name, gstr->str, -1, &error))
+			g_warning ("Error setting all apps file:%s\n", error->message);
+
+		g_string_free (gstr, TRUE);
+		g_free (all_apps_file_name);
+		return;
+	}
+
+	all_apps_cache = g_hash_table_new (g_str_hash, g_str_equal);
+	all_apps_split = g_strsplit (all_apps, separator, -1);
+	for (x = 0; all_apps_split[x]; x++)
+	{
+		g_hash_table_insert (all_apps_cache, all_apps_split[x], all_apps_split[x]);
+	}
+
+	got_new_apps = FALSE;
+	new_apps_dups = g_hash_table_new (g_str_hash, g_str_equal);
+	for (categories = app_data->categories_list; categories; categories = categories->next)
+	{
+		CategoryData *cat_data = categories->data;
+		for (launchers = cat_data->launcher_list; launchers; launchers = launchers->next)
+		{
+			Tile *tile = TILE (launchers->data);
+			GnomeDesktopItem *item =
+				application_tile_get_desktop_item (APPLICATION_TILE (tile));
+			const gchar *uri = gnome_desktop_item_get_location (item);
+			if (!g_hash_table_lookup (all_apps_cache, uri))
+			{
+				GFile *file;
+				GFileInfo *info;
+				long filetime;
+
+				if (g_hash_table_lookup (new_apps_dups, uri))
+				{
+					/* if a desktop file is in 2 or more top level categories, only show it once */
+					/* printf("Discarding Newapp duplicate:%s\n", uri); */
+					break;
+				}
+				g_hash_table_insert (new_apps_dups, (gpointer) uri, (gpointer) uri);
+
+				if (!got_new_apps)
+				{
+					new_apps_category = g_new0 (CategoryData, 1);
+					new_apps_category->category =
+						g_strdup (app_data->new_apps->name);
+					app_data->new_apps->garray =
+						g_array_sized_new (FALSE, TRUE,
+						sizeof (NewAppData *),
+						app_data->new_apps->max_items);
+
+					/* should not need this, but a bug in glib does not actually clear the elements until you call this method */
+					g_array_set_size (app_data->new_apps->garray, app_data->new_apps->max_items);
+					got_new_apps = TRUE;
+				}
+
+				file = g_file_new_for_uri (uri);
+				info = g_file_query_info (file,
+							  G_FILE_ATTRIBUTE_TIME_MODIFIED,
+							  0, NULL, NULL);
+
+				if (!info)
+				{
+					g_object_unref (file);
+					g_warning ("Cant get vfs info for %s\n", uri);
+					return;
+				}
+				filetime = (long) g_file_info_get_attribute_uint64 (info,
+										    G_FILE_ATTRIBUTE_TIME_MODIFIED);
+				g_object_unref (info);
+				g_object_unref (file);
+
+				for (x = 0; x < app_data->new_apps->max_items; x++)
+				{
+					NewAppData *temp_data = (NewAppData *)
+						g_array_index (app_data->new_apps->garray, NewAppData *, x);
+					if (!temp_data || filetime > temp_data->time)	/* if this slot is empty or we are newer than this slot */
+					{
+						NewAppData *temp = g_new0 (NewAppData, 1);
+						temp->time = filetime;
+						temp->item = item;
+						g_array_insert_val (app_data->new_apps->garray, x,
+							temp);
+						break;
+					}
+				}
+			}
+		}
+	}
+	g_hash_table_destroy (new_apps_dups);
+	g_hash_table_destroy (all_apps_cache);
+
+	if (got_new_apps)
+	{
+		for (x = 0; x < app_data->new_apps->max_items; x++)
+		{
+			NewAppData *data =
+				(NewAppData *) g_array_index (app_data->new_apps->garray,
+				NewAppData *, x);
+			if (data)
+			{
+				insert_launcher_into_category (new_apps_category, data->item,
+					app_data);
+				g_free (data);
+			}
+			else
+				break;
+		}
+		app_data->categories_list =
+			g_list_prepend (app_data->categories_list, new_apps_category);
+
+		g_array_free (app_data->new_apps->garray, TRUE);
+	}
+	g_free (all_apps);
+	g_free (all_apps_file_name);
+	g_strfreev (all_apps_split);
+}
+
+static void
+insert_launcher_into_category (CategoryData * cat_data, GnomeDesktopItem * desktop_item,
+	AppShellData * app_data)
+{
+	GtkWidget *launcher;
+	static GtkSizeGroup *icon_group = NULL;
+
+	gchar *filepath;
+	gchar *filename;
+	GtkWidget *tile_icon;
+
+	if (!icon_group)
+		icon_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+	launcher =
+		application_tile_new_full (gnome_desktop_item_get_location (desktop_item),
+		app_data->icon_size, app_data->show_tile_generic_name, app_data->gconf_prefix);
+	gtk_widget_set_size_request (launcher, SIZING_TILE_WIDTH, -1);
+
+	filepath =
+		g_strdup (gnome_desktop_item_get_string (desktop_item, GNOME_DESKTOP_ITEM_EXEC));
+	g_strdelimit (filepath, " ", '\0');	/* just want the file name - no args or replacements */
+	filename = g_strrstr (filepath, "/");
+	if (filename)
+		g_stpcpy (filepath, filename + 1);
+	filename = g_ascii_strdown (filepath, -1);
+	g_free (filepath);
+	g_object_set_data (G_OBJECT (launcher), TILE_EXEC_NAME, filename);
+
+	tile_icon = NAMEPLATE_TILE (launcher)->image;
+	gtk_size_group_add_widget (icon_group, tile_icon);
+
+	g_signal_connect (launcher, "tile-activated", G_CALLBACK (tile_activated_cb), app_data);
+
+	/* Note that this will handle the case of the action being launched via the side panel as
+	   well as directly from the context menu of an individual launcher, because they both
+	   funnel through tile_button_action_activate.
+	*/
+	g_signal_connect (launcher, "tile-action-triggered",
+		G_CALLBACK (handle_menu_action_performed), app_data);
+
+	/* These will be inserted/removed from tables as the filter changes and we dont want them */
+	/* destroyed when they are removed */
+	g_object_ref (launcher);
+
+	/* use alphabetical order instead of the gmenu order. We group all sub items in each top level
+	category together, ignoring sub menus, so we also ignore sub menu layout hints */
+	cat_data->launcher_list =
+		/* g_list_insert (cat_data->launcher_list, launcher, -1); */
+		g_list_insert_sorted (cat_data->launcher_list, launcher, application_launcher_compare);
+	cat_data->filtered_launcher_list =
+		/* g_list_insert (cat_data->filtered_launcher_list, launcher, -1); */
+		g_list_insert_sorted (cat_data->filtered_launcher_list, launcher, application_launcher_compare);
+}
+
+static gint
+application_launcher_compare (gconstpointer a, gconstpointer b)
+{
+	ApplicationTile *launcher1 = APPLICATION_TILE (a);
+	ApplicationTile *launcher2 = APPLICATION_TILE (b);
+
+	gchar *val1 = launcher1->name;
+	gchar *val2 = launcher2->name;
+
+	if (val1 == NULL || val2 == NULL)
+	{
+		g_assert_not_reached ();
+	}
+	return g_ascii_strcasecmp (val1, val2);
+}
+
+static void
+application_launcher_clear_search_bar (AppShellData * app_data)
+{
+	SlabSection *section = SLAB_SECTION (app_data->filter_section);
+	NldSearchBar *search_bar;
+	g_assert (NLD_IS_SEARCH_BAR (section->contents));
+	search_bar = NLD_SEARCH_BAR (section->contents);
+	nld_search_bar_set_text (search_bar, "", TRUE);
+}
+
+/*
+static gint
+category_name_compare (gconstpointer a, gconstpointer b)
+{
+	CategoryData *data = (CategoryData *) a;
+	const gchar *category = b;
+
+	if (category == NULL || data->category == NULL)
+	{
+		g_assert_not_reached ();
+	}
+	return g_ascii_strcasecmp (category, data->category);
+}
+*/
+
+static void
+tile_activated_cb (Tile * tile, TileEvent * event, gpointer user_data)
+{
+	switch (event->type)
+	{
+	case TILE_EVENT_ACTIVATED_SINGLE_CLICK:
+	case TILE_EVENT_ACTIVATED_KEYBOARD:
+		handle_launcher_single_clicked (tile, user_data);
+		break;
+	default:
+		break;
+	}
+
+}
+
+static void
+handle_launcher_single_clicked (Tile * launcher, gpointer data)
+{
+	AppShellData *app_data = (AppShellData *) data;
+	gchar *gconf_key;
+
+	tile_trigger_action (launcher, launcher->actions[APPLICATION_TILE_ACTION_START]);
+
+	gconf_key = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_START);
+	if (get_slab_gconf_bool (gconf_key))
+	{
+		if (app_data->exit_on_close)
+			gtk_main_quit ();
+		else
+			hide_shell (app_data);
+	}
+	g_free (gconf_key);
+}
+
+static void
+handle_menu_action_performed (Tile * launcher, TileEvent * event, TileAction * action,
+	gpointer data)
+{
+	AppShellData *app_data = (AppShellData *) data;
+	gchar *temp;
+
+	temp = NULL;
+	if (action == launcher->actions[APPLICATION_TILE_ACTION_START])
+	{
+		temp = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_START);
+	}
+
+	else if (action == launcher->actions[APPLICATION_TILE_ACTION_HELP])
+	{
+		temp = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_HELP);
+	}
+
+	else if (action == launcher->actions[APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]
+		|| action == launcher->actions[APPLICATION_TILE_ACTION_UPDATE_STARTUP])
+	{
+		temp = g_strdup_printf ("%s%s", app_data->gconf_prefix,
+			EXIT_SHELL_ON_ACTION_ADD_REMOVE);
+	}
+
+	else if (action == launcher->actions[APPLICATION_TILE_ACTION_UPGRADE_PACKAGE]
+		|| action == launcher->actions[APPLICATION_TILE_ACTION_UNINSTALL_PACKAGE])
+	{
+		temp = g_strdup_printf ("%s%s", app_data->gconf_prefix,
+			EXIT_SHELL_ON_ACTION_UPGRADE_UNINSTALL);
+	}
+
+	if (temp)
+	{
+		if (get_slab_gconf_bool (temp))
+		{
+			if (app_data->exit_on_close)
+				gtk_main_quit ();
+			else
+				hide_shell (app_data);
+		}
+		g_free (temp);
+	}
+	else
+		g_warning ("Unknown Action");
+}
diff --git a/libslab/app-shell.h b/libslab/app-shell.h
new file mode 100644
index 0000000..2add230
--- /dev/null
+++ b/libslab/app-shell.h
@@ -0,0 +1,140 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __APP_SHELL_H__
+#define __APP_SHELL_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#define GMENU_I_KNOW_THIS_IS_UNSTABLE
+#include <gmenu-tree.h>
+
+#include "slab-section.h"
+#include "tile.h"
+
+G_BEGIN_DECLS
+
+#define CATEGORY_SPACING 0
+#define GROUP_POSITION_NUMBER_KEY "Unique Group Position Number"
+#define APP_ACTION_KEY  "Unique Application Action Key"
+
+/* constants for initial sizing */
+#define SIZING_SCREEN_WIDTH_LARGE  1024
+#define SIZING_SCREEN_WIDTH_MEDIUM 800
+#define SIZING_SCREEN_WIDTH_SMALL  640
+#define SIZING_SCREEN_WIDTH_LARGE_NUMCOLS  3
+#define SIZING_SCREEN_WIDTH_MEDIUM_NUMCOLS 2
+#define SIZING_SCREEN_WIDTH_SMALL_NUMCOLS  1
+#define SIZING_TILE_WIDTH 230
+#define SIZING_HEIGHT_PERCENT 0.8
+
+typedef struct
+{
+	const gchar *name;
+	gint max_items;
+	GArray *garray;
+} NewAppConfig;
+
+typedef struct _AppShellData
+{
+	GtkWidget *main_gnome_app;
+	gint main_gnome_app_window_x;
+	gint main_gnome_app_window_y;
+	gboolean main_gnome_app_window_shown_once;
+
+	GtkWidget *shell;
+	GtkWidget *groups_section;
+
+	GtkWidget *actions_section;
+	/*
+		NULL      - if the available actions depend on the current tile selected
+		NON-NULL  - a list of AppAction that are always shown
+	*/
+	GSList *static_actions;
+
+	GtkWidget *filter_section;
+	gchar *filter_string;
+	GdkCursor *busy_cursor;
+
+	GtkWidget *category_layout;
+	GList *categories_list;
+	GList *cached_tables_list;	/* list of currently showing (not filtered out) tables */
+	Tile *last_clicked_launcher;
+	SlabSection *selected_group;
+	GtkIconSize icon_size;
+	const gchar *gconf_prefix;
+	const gchar *menu_name;
+	NewAppConfig *new_apps;
+	GMenuTree *tree;
+	GHashTable *hash;
+
+	guint filter_changed_timeout;
+	gboolean stop_incremental_relayout;
+	GList *incremental_relayout_cat_list;
+	gboolean filtered_out_everything;
+	GtkWidget *filtered_out_everything_widget;
+	GtkLabel *filtered_out_everything_widget_label;
+
+	gboolean show_tile_generic_name;
+	gboolean exit_on_close;
+} AppShellData;
+
+typedef struct
+{
+	gchar *category;
+	Tile *group_launcher;
+
+	SlabSection *section;
+	GList *launcher_list;
+	GList *filtered_launcher_list;
+} CategoryData;
+
+typedef struct
+{
+	const gchar *name;
+	GnomeDesktopItem *item;
+} AppAction;
+
+typedef struct
+{
+	long time;
+	GnomeDesktopItem *item;
+} NewAppData;
+
+void generate_categories (AppShellData * app_data);
+
+/* If new_apps is NULL then the new applications category is not created */
+AppShellData *appshelldata_new (const gchar * menu_name, NewAppConfig * new_apps,
+	const gchar * gconf_keys_prefix, GtkIconSize icon_size,
+	gboolean show_tile_generic_name, gboolean exit_on_close);
+
+void layout_shell (AppShellData * app_data, const gchar * filter_title, const gchar * groups_title,
+	const gchar * actions_title, GSList * actions,
+	void (*actions_handler) (Tile *, TileEvent *, gpointer));
+
+gboolean create_main_window (AppShellData * app_data, const gchar * app_name, const gchar * title,
+	const gchar * window_icon, gint width, gint height, gboolean hidden);
+
+void hide_shell (AppShellData * app_data);
+
+void show_shell (AppShellData * app_data);
+
+G_END_DECLS
+#endif /* __APP_SHELL_H__ */
diff --git a/libslab/application-tile.c b/libslab/application-tile.c
new file mode 100644
index 0000000..5b94497
--- /dev/null
+++ b/libslab/application-tile.c
@@ -0,0 +1,882 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006, 2007 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "application-tile.h"
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gconf/gconf-client.h>
+#include <unistd.h>
+
+#include "slab-gnome-util.h"
+#include "libslab-utils.h"
+#include "bookmark-agent.h"
+#include "themed-icon.h"
+
+G_DEFINE_TYPE (ApplicationTile, application_tile, NAMEPLATE_TILE_TYPE)
+
+typedef enum {
+	APP_IN_USER_STARTUP_DIR,
+	APP_NOT_IN_STARTUP_DIR,
+	APP_NOT_ELIGIBLE
+} StartupStatus;
+
+static void application_tile_get_property (GObject *, guint,       GValue *, GParamSpec *);
+static void application_tile_set_property (GObject *, guint, const GValue *, GParamSpec *);
+static void application_tile_finalize     (GObject *);
+
+static void application_tile_setup (ApplicationTile *, const gchar *);
+
+static GtkWidget *create_header    (const gchar *);
+static GtkWidget *create_subheader (const gchar *);
+
+static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer);
+
+static void start_trigger     (Tile *, TileEvent *, TileAction *);
+static void help_trigger      (Tile *, TileEvent *, TileAction *);
+static void user_apps_trigger (Tile *, TileEvent *, TileAction *);
+static void startup_trigger   (Tile *, TileEvent *, TileAction *);
+static void upgrade_trigger   (Tile *, TileEvent *, TileAction *);
+static void uninstall_trigger (Tile *, TileEvent *, TileAction *);
+
+static void add_to_user_list         (ApplicationTile *);
+static void remove_from_user_list    (ApplicationTile *);
+static void add_to_startup_list      (ApplicationTile *);
+static void remove_from_startup_list (ApplicationTile *);
+
+static gboolean verify_package_management_command (const gchar *);
+static void run_package_management_command (ApplicationTile *, gchar *);
+
+static void update_user_list_menu_item (ApplicationTile *);
+static void agent_notify_cb (GObject *, GParamSpec *, gpointer);
+
+static StartupStatus get_desktop_item_startup_status (GnomeDesktopItem *);
+static void          update_startup_menu_item (ApplicationTile *);
+
+typedef struct {
+	GnomeDesktopItem *desktop_item;
+
+	gchar       *image_id;
+	gboolean     image_is_broken;
+	GtkIconSize  image_size;
+
+	gboolean show_generic_name;
+	StartupStatus startup_status;
+
+	BookmarkAgent       *agent;
+	BookmarkStoreStatus  agent_status;
+	gboolean             is_bookmarked;
+	gulong               notify_signal_id;
+} ApplicationTilePrivate;
+
+#define APPLICATION_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APPLICATION_TILE_TYPE, ApplicationTilePrivate))
+
+enum {
+	PROP_0,
+	PROP_APPLICATION_NAME,
+	PROP_APPLICATION_DESCRIPTION,
+	PROP_APPLICATION_GCONF_PREFIX
+};
+
+static void
+application_tile_class_init (ApplicationTileClass *app_tile_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (app_tile_class);
+
+	g_obj_class->get_property = application_tile_get_property;
+	g_obj_class->set_property = application_tile_set_property;
+	g_obj_class->finalize     = application_tile_finalize;
+
+	g_type_class_add_private (app_tile_class, sizeof (ApplicationTilePrivate));
+
+	g_object_class_install_property (
+		g_obj_class, PROP_APPLICATION_NAME,
+		g_param_spec_string (
+			"application-name", "application-name",
+			"the name of the application", NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		g_obj_class, PROP_APPLICATION_DESCRIPTION,
+		g_param_spec_string (
+			"application-description", "application-description",
+			"the name of the application", NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		g_obj_class, PROP_APPLICATION_GCONF_PREFIX,
+		g_param_spec_string (
+			"gconf-prefix", "gconf-prefix",
+			"configuration prefix", NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+GtkWidget *
+application_tile_new (const gchar *desktop_item_id)
+{
+	return application_tile_new_full (desktop_item_id, GTK_ICON_SIZE_DND, TRUE, NULL);
+}
+
+GtkWidget *
+application_tile_new_full (const gchar *desktop_item_id,
+	GtkIconSize image_size, gboolean show_generic_name, const gchar *gconf_prefix)
+{
+	ApplicationTile        *this;
+	ApplicationTilePrivate *priv;
+
+	const gchar *uri = NULL;
+
+	GnomeDesktopItem *desktop_item;
+
+
+	desktop_item = load_desktop_item_from_unknown (desktop_item_id);
+
+	if (
+		desktop_item &&
+		gnome_desktop_item_get_entry_type (desktop_item) == GNOME_DESKTOP_ITEM_TYPE_APPLICATION
+	)
+		uri = gnome_desktop_item_get_location (desktop_item);
+
+	if (! uri) {
+		if (desktop_item)
+			gnome_desktop_item_unref (desktop_item);
+
+		return NULL;
+	}
+
+	this = g_object_new (APPLICATION_TILE_TYPE, "tile-uri", uri, NULL);
+	priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	priv->image_size   = image_size;
+	priv->desktop_item = desktop_item;
+	priv->show_generic_name = show_generic_name;
+
+	application_tile_setup (this, gconf_prefix);
+
+	return GTK_WIDGET (this);
+}
+
+static void
+application_tile_init (ApplicationTile *tile)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (tile);
+
+	priv->desktop_item    = NULL;
+	priv->image_id        = NULL;
+	priv->image_is_broken = TRUE;
+
+	priv->agent            = NULL;
+	priv->agent_status     = BOOKMARK_STORE_ABSENT;
+	priv->is_bookmarked    = FALSE;
+	priv->notify_signal_id = 0;
+
+	tile->name = tile->description = tile->gconf_prefix = NULL;
+}
+
+static void
+application_tile_finalize (GObject *g_object)
+{
+	ApplicationTile *tile = APPLICATION_TILE (g_object);
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (g_object);
+
+	if (tile->name) {
+		g_free (tile->name);
+		tile->name = NULL;
+	}
+	if (tile->description) {
+		g_free (tile->description);
+		tile->description = NULL;
+	}
+	if (tile->gconf_prefix) {
+		g_free (tile->gconf_prefix);
+		tile->gconf_prefix = NULL;
+	}
+
+	if (priv->desktop_item) {
+		gnome_desktop_item_unref (priv->desktop_item);
+		priv->desktop_item = NULL;
+	}
+	if (priv->image_id) {
+		g_free (priv->image_id);
+		priv->image_id = NULL;
+	}
+
+	if (priv->notify_signal_id)
+		g_signal_handler_disconnect (priv->agent, priv->notify_signal_id);
+
+	g_object_unref (G_OBJECT (priv->agent));
+
+	G_OBJECT_CLASS (application_tile_parent_class)->finalize (g_object);
+}
+
+static void
+application_tile_get_property (GObject *g_obj, guint prop_id, GValue *value, GParamSpec *param_spec)
+{
+	ApplicationTile *tile = APPLICATION_TILE (g_obj);
+
+	switch (prop_id) {
+	case PROP_APPLICATION_NAME:
+		g_value_set_string (value, tile->name);
+		break;
+
+	case PROP_APPLICATION_DESCRIPTION:
+		g_value_set_string (value, tile->description);
+		break;
+
+	case PROP_APPLICATION_GCONF_PREFIX:
+		g_value_set_string (value, tile->gconf_prefix);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+application_tile_set_property (GObject *g_obj, guint prop_id, const GValue *value, GParamSpec *param_spec)
+{
+	ApplicationTile *tile = APPLICATION_TILE (g_obj);
+
+	switch (prop_id) {
+	case PROP_APPLICATION_NAME:
+		if (tile->name)
+			g_free (tile->name);
+		tile->name = g_strdup (g_value_get_string (value));
+		break;
+
+	case PROP_APPLICATION_DESCRIPTION:
+		if (tile->description)
+			g_free (tile->description);
+		tile->description = g_strdup (g_value_get_string (value));
+		break;
+
+	case PROP_APPLICATION_GCONF_PREFIX:
+		if (tile->gconf_prefix)
+			g_free (tile->gconf_prefix);
+		tile->gconf_prefix = g_strdup (g_value_get_string (value));
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+application_tile_setup (ApplicationTile *this, const gchar *gconf_prefix)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	GtkWidget *image;
+	GtkWidget *header;
+	GtkWidget *subheader;
+	GtkMenu   *context_menu;
+	AtkObject *accessible;
+
+	TileAction  **actions;
+	TileAction   *action;
+	GtkWidget    *menu_item;
+	GtkContainer *menu_ctnr;
+
+	const gchar *name;
+	const gchar *desc;
+
+	const gchar *comment;
+
+	const gchar *key;
+	gchar *markup;
+
+	/*Fixme - need to address the entire gconf key location issue */
+	/*Fixme - this is just a temporary stop gap                   */
+	gboolean use_new_prefix;
+
+
+	if (! priv->desktop_item) {
+		priv->desktop_item = load_desktop_item_from_unknown (TILE (this)->uri);
+
+		if (! priv->desktop_item)
+			return;
+	}
+
+	priv->image_id = g_strdup (gnome_desktop_item_get_localestring (priv->desktop_item, "Icon"));
+	image = themed_icon_new (priv->image_id, priv->image_size);
+
+	name = gnome_desktop_item_get_localestring (priv->desktop_item, "Name");
+	desc = gnome_desktop_item_get_localestring (priv->desktop_item, "GenericName");
+	comment = gnome_desktop_item_get_localestring (priv->desktop_item, "Comment");	
+
+	accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
+	if (name)
+	  atk_object_set_name (accessible, name);
+	if (desc)
+	  atk_object_set_description (accessible, desc);
+
+	header    = create_header    (name);
+
+	/*if no GenericName then just show and center the Name */
+	if (desc && priv->show_generic_name
+	    && (!name || strcmp(name, desc) != 0))
+		subheader = create_subheader (desc);
+	else
+		subheader = NULL;
+
+	context_menu = GTK_MENU (gtk_menu_new ());
+
+	g_object_set (
+		G_OBJECT (this),
+		"nameplate-image",         image,
+		"nameplate-header",        header,
+		"nameplate-subheader",     subheader,
+		"context-menu",            context_menu,
+		"application-name",        name,
+		"application-description", desc,
+		"gconf-prefix",            gconf_prefix,
+		NULL);
+	gtk_widget_set_tooltip_text (GTK_WIDGET (this), comment);
+
+	priv->agent = bookmark_agent_get_instance (BOOKMARK_STORE_USER_APPS);
+	g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL);
+
+	priv->notify_signal_id = g_signal_connect (
+		G_OBJECT (priv->agent), "notify", G_CALLBACK (agent_notify_cb), this);
+
+	priv->startup_status  = get_desktop_item_startup_status (priv->desktop_item);
+
+	actions = g_new0 (TileAction *, 6);
+
+	TILE (this)->actions   = actions;
+	TILE (this)->n_actions = 6;
+
+	menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
+
+/* make start action */
+
+	markup = g_markup_printf_escaped (_("<b>Start %s</b>"), this->name);
+	action = tile_action_new (TILE (this), start_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
+	actions [APPLICATION_TILE_ACTION_START] = action;
+	g_free (markup);
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	TILE (this)->default_action = action;
+
+/* insert separator */
+
+	gtk_container_add (menu_ctnr, gtk_separator_menu_item_new ());
+
+/* make help action */
+
+	if (gnome_desktop_item_get_string (priv->desktop_item, "DocPath")) {
+		action = tile_action_new (
+			TILE (this), help_trigger, _("Help"),
+			TILE_ACTION_OPENS_NEW_WINDOW | TILE_ACTION_OPENS_HELP);
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_container_add (menu_ctnr, menu_item);
+	}
+	else {
+		action = NULL;
+	}
+
+	actions [APPLICATION_TILE_ACTION_HELP] = action;
+
+/* insert separator */
+
+	if (action != NULL)
+		gtk_container_add (menu_ctnr, gtk_separator_menu_item_new ());
+
+/* make "add/remove to favorites" action */
+
+	update_user_list_menu_item (this);
+
+/* make "add/remove to startup" action */
+
+	if (priv->startup_status != APP_NOT_ELIGIBLE) {
+		action = tile_action_new (TILE (this), startup_trigger, NULL, 0);
+		actions [APPLICATION_TILE_ACTION_UPDATE_STARTUP] = action;
+
+		update_startup_menu_item (this);
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+		gtk_container_add (menu_ctnr, menu_item);
+	}
+
+/* make upgrade action */
+
+	if (this->gconf_prefix && ! g_str_has_prefix (this->gconf_prefix, "/desktop/"))
+		use_new_prefix = TRUE;
+	else
+		use_new_prefix = FALSE;
+
+	if(!use_new_prefix)
+		key = SLAB_UPGRADE_PACKAGE_KEY;
+	else
+		key = "/apps/main-menu/upgrade_package_command";
+
+	if (verify_package_management_command (key)) {
+		action = tile_action_new (TILE (this), upgrade_trigger, _("Upgrade"), TILE_ACTION_OPENS_NEW_WINDOW);
+		actions [APPLICATION_TILE_ACTION_UPGRADE_PACKAGE] = action;
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_container_add (menu_ctnr, menu_item);
+	} else
+		actions [APPLICATION_TILE_ACTION_UPGRADE_PACKAGE] = NULL;
+
+/* make uninstall action */
+
+	if(!use_new_prefix)
+		key = SLAB_UNINSTALL_PACKAGE_KEY;
+	else
+		key = "/apps/main-menu/uninstall_package_command";
+
+	if (verify_package_management_command (key)) {
+		action = tile_action_new (TILE (this), uninstall_trigger, _("Uninstall"), TILE_ACTION_OPENS_NEW_WINDOW);
+		actions [APPLICATION_TILE_ACTION_UNINSTALL_PACKAGE] = action;
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_container_add (menu_ctnr, menu_item);
+	} else
+		actions [APPLICATION_TILE_ACTION_UNINSTALL_PACKAGE] = NULL;
+
+	gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu));
+}
+
+static GtkWidget *
+create_header (const gchar *name)
+{
+	GtkWidget *header;
+
+
+	header = gtk_label_new (name);
+	gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	g_signal_connect (
+		G_OBJECT (header),
+		"size-allocate",
+		G_CALLBACK (header_size_allocate_cb),
+		NULL);
+
+	return header;
+}
+
+static GtkWidget *
+create_subheader (const gchar *desc)
+{
+	GtkWidget *subheader;
+
+
+	subheader = gtk_label_new (desc);
+	gtk_label_set_ellipsize (GTK_LABEL (subheader), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (subheader), 0.0, 0.5);
+	gtk_widget_modify_fg (
+		subheader,
+		GTK_STATE_NORMAL,
+		& subheader->style->fg [GTK_STATE_INSENSITIVE]);
+
+	return subheader;
+}
+
+static void
+start_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	open_desktop_item_exec (APPLICATION_TILE_GET_PRIVATE (tile)->desktop_item);
+}
+
+static void
+help_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	open_desktop_item_help (APPLICATION_TILE_GET_PRIVATE (tile)->desktop_item);
+}
+
+static void
+user_apps_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	ApplicationTile *this = APPLICATION_TILE (tile);
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	if (priv->is_bookmarked)
+		remove_from_user_list (this);
+	else
+		add_to_user_list (this);
+
+	update_user_list_menu_item (this);
+}
+
+static void
+add_to_user_list (ApplicationTile *this)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	BookmarkItem *item;
+
+
+	item = g_new0 (BookmarkItem, 1);
+	item->uri       = TILE (this)->uri;
+	item->mime_type = "application/x-desktop";
+
+	bookmark_agent_add_item (priv->agent, item);
+	g_free (item);
+
+	priv->is_bookmarked = TRUE;
+}
+
+static void
+remove_from_user_list (ApplicationTile *this)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	bookmark_agent_remove_item (priv->agent, TILE (this)->uri);
+
+	priv->is_bookmarked = FALSE;
+}
+
+static void
+upgrade_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	run_package_management_command (APPLICATION_TILE (tile), SLAB_UPGRADE_PACKAGE_KEY);
+}
+
+static void
+uninstall_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	run_package_management_command (APPLICATION_TILE (tile), SLAB_UNINSTALL_PACKAGE_KEY);
+}
+
+static gboolean
+verify_package_management_command (const gchar *gconf_key)
+{
+	gchar *cmd;
+	gchar *path;
+	gchar *args;
+
+	gboolean retval;
+
+	cmd = get_slab_gconf_string (gconf_key);
+	if (!cmd)
+		return FALSE;
+
+	args = strchr (cmd, ' ');
+
+	if (args)
+		*args = '\0';
+
+	path = g_find_program_in_path (cmd);
+
+	retval = (path != NULL);
+
+	g_free (cmd);
+	g_free (path);
+
+	return retval;
+}
+
+static void
+run_package_management_command (ApplicationTile *tile, gchar *gconf_key)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (tile);
+
+	gchar *cmd_precis;
+	gchar *package_name;
+
+	GString *cmd;
+	gint pivot;
+	gchar **argv;
+
+	GError *error = NULL;
+
+	package_name = get_package_name_from_desktop_item (priv->desktop_item);
+
+	if (!package_name)
+		return;
+
+	cmd_precis = get_slab_gconf_string (gconf_key);
+
+	g_assert (cmd_precis);
+
+	pivot = strstr (cmd_precis, "PACKAGE_NAME") - cmd_precis;
+
+	cmd = g_string_new_len (cmd_precis, pivot);
+	g_string_append (cmd, package_name);
+	g_string_append (cmd, & cmd_precis [pivot + 12]);
+
+	argv = g_strsplit (cmd->str, " ", -1);
+
+	g_string_free (cmd, TRUE);
+
+	g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
+
+	if (error) {
+		g_warning ("error: [%s]\n", error->message);
+
+		g_error_free (error);
+	}
+
+	g_free (cmd_precis);
+	g_free (package_name);
+	g_strfreev (argv);
+}
+
+static void
+startup_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	ApplicationTile *this = APPLICATION_TILE (tile);
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	switch (priv->startup_status) {
+		case APP_IN_USER_STARTUP_DIR:
+			remove_from_startup_list (this);
+			break;
+
+		case APP_NOT_IN_STARTUP_DIR:
+			add_to_startup_list (this);
+			break;
+
+		default:
+			break;
+	}
+
+	update_startup_menu_item (this);
+}
+
+static void
+add_to_startup_list (ApplicationTile *this)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	gchar *desktop_item_filename;
+	gchar *desktop_item_basename;
+
+	gchar *startup_dir;
+	gchar *dst_filename;
+
+	const gchar *src_uri;
+	gchar *dst_uri;
+
+	desktop_item_filename =
+		g_filename_from_uri (gnome_desktop_item_get_location (priv->desktop_item), NULL,
+		NULL);
+
+	g_return_if_fail (desktop_item_filename != NULL);
+
+	desktop_item_basename = g_path_get_basename (desktop_item_filename);
+
+	startup_dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
+
+	if (! g_file_test (startup_dir, G_FILE_TEST_EXISTS))
+		g_mkdir_with_parents (startup_dir, 0700);
+
+	dst_filename = g_build_filename (startup_dir, desktop_item_basename, NULL);
+
+	src_uri = gnome_desktop_item_get_location (priv->desktop_item);
+	dst_uri = g_filename_to_uri (dst_filename, NULL, NULL);
+
+	copy_file (src_uri, dst_uri);
+	priv->startup_status = APP_IN_USER_STARTUP_DIR;
+
+	g_free (desktop_item_filename);
+	g_free (desktop_item_basename);
+	g_free (startup_dir);
+	g_free (dst_filename);
+	g_free (dst_uri);
+}
+
+static void
+remove_from_startup_list (ApplicationTile *this)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	gchar *ditem_filename;
+	gchar *ditem_basename;
+	gchar *src_filename;
+
+	ditem_filename =
+		g_filename_from_uri (gnome_desktop_item_get_location (priv->desktop_item), NULL,
+		NULL);
+
+	g_return_if_fail (ditem_filename != NULL);
+
+	ditem_basename = g_path_get_basename (ditem_filename);
+
+	src_filename = g_build_filename (g_get_user_config_dir (), "autostart", ditem_basename, NULL);
+
+	priv->startup_status = APP_NOT_IN_STARTUP_DIR;
+	if (g_file_test (src_filename, G_FILE_TEST_EXISTS))
+	{
+		if(g_file_test (src_filename, G_FILE_TEST_IS_DIR))
+			g_assert_not_reached ();
+		g_unlink (src_filename);
+	}
+
+	g_free (ditem_filename);
+	g_free (ditem_basename);
+	g_free (src_filename);
+}
+
+GnomeDesktopItem *
+application_tile_get_desktop_item (ApplicationTile *tile)
+{
+	return APPLICATION_TILE_GET_PRIVATE (tile)->desktop_item;
+}
+
+static void
+update_user_list_menu_item (ApplicationTile *this)
+{
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	TileAction *action;
+	GtkWidget  *item;
+
+
+	if (priv->agent_status == BOOKMARK_STORE_ABSENT) {
+		if (TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU])
+			g_object_unref (TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]);
+
+		TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU] = NULL;
+	}
+	else if (! TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]) {
+		TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU] =
+			tile_action_new (TILE (this), user_apps_trigger, NULL, 0);
+
+		tile_action_set_menu_item_label (
+			TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU], "blah");
+
+		item = GTK_WIDGET (tile_action_get_menu_item (
+			TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]));
+		gtk_menu_shell_insert (GTK_MENU_SHELL (TILE (this)->context_menu), item, 4);
+
+		gtk_widget_show_all (item);
+	}
+	else
+		/* do nothing */ ;
+
+	action = TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU];
+
+	if (! action)
+		return;
+
+	priv->is_bookmarked = bookmark_agent_has_item (priv->agent, TILE (this)->uri);
+
+	if (priv->is_bookmarked)
+		tile_action_set_menu_item_label (action, _("Remove from Favorites"));
+	else
+		tile_action_set_menu_item_label (action, _("Add to Favorites"));
+
+	item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	if (! GTK_IS_MENU_ITEM (item))
+		return;
+
+	g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL);
+
+	gtk_widget_set_sensitive (item, (priv->agent_status != BOOKMARK_STORE_DEFAULT_ONLY));
+}
+
+static StartupStatus
+get_desktop_item_startup_status (GnomeDesktopItem *desktop_item)
+{
+	gchar *filename;
+	gchar *basename;
+
+	const gchar * const * global_dirs;
+	gchar *global_target;
+	gchar *user_target;
+
+	StartupStatus retval;
+	gint x;
+	
+	filename = g_filename_from_uri (gnome_desktop_item_get_location (desktop_item), NULL, NULL);
+	if (!filename)
+		return APP_NOT_ELIGIBLE;
+	basename = g_path_get_basename (filename);
+
+	retval = APP_NOT_IN_STARTUP_DIR;
+	global_dirs = g_get_system_config_dirs();
+	for(x=0; global_dirs[x]; x++)
+	{
+		global_target = g_build_filename (global_dirs[x], "autostart", basename, NULL);
+		if (g_file_test (global_target, G_FILE_TEST_EXISTS))
+		{
+			retval = APP_NOT_ELIGIBLE;
+			g_free (global_target);
+			break;
+		}
+		g_free (global_target);
+	}
+
+	/* gnome-session currently checks these dirs also. see startup-programs.c */
+	if (retval != APP_NOT_ELIGIBLE)
+	{
+		global_dirs = g_get_system_data_dirs();
+		for(x=0; global_dirs[x]; x++)
+		{
+			global_target = g_build_filename (global_dirs[x], "gnome", "autostart", basename, NULL);
+			if (g_file_test (global_target, G_FILE_TEST_EXISTS))
+			{
+				retval = APP_NOT_ELIGIBLE;
+				g_free (global_target);
+				break;
+			}
+			g_free (global_target);
+		}
+	}
+
+	if (retval != APP_NOT_ELIGIBLE)
+	{
+		user_target = g_build_filename (g_get_user_config_dir (), "autostart", basename, NULL);
+		if (g_file_test (user_target, G_FILE_TEST_EXISTS))
+			retval = APP_IN_USER_STARTUP_DIR;
+		g_free (user_target);
+	}
+
+	g_free (basename);
+	g_free (filename);
+
+	return retval;
+}
+
+static void
+update_startup_menu_item (ApplicationTile *this)
+{
+	TileAction *action = TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_STARTUP];
+	ApplicationTilePrivate *priv = APPLICATION_TILE_GET_PRIVATE (this);
+
+	if (!action)
+		return;
+
+	if (priv->startup_status == APP_IN_USER_STARTUP_DIR)
+		tile_action_set_menu_item_label (action, _("Remove from Startup Programs"));
+	else
+		tile_action_set_menu_item_label (action, _("Add to Startup Programs"));
+}
+
+static void
+header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
+{
+	gtk_widget_set_size_request (widget, alloc->width, -1);
+}
+
+static void
+agent_notify_cb (GObject *g_obj, GParamSpec *pspec, gpointer user_data)
+{
+	update_user_list_menu_item (APPLICATION_TILE (user_data));
+}
diff --git a/libslab/application-tile.h b/libslab/application-tile.h
new file mode 100644
index 0000000..57a97d9
--- /dev/null
+++ b/libslab/application-tile.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __APPLICATION_TILE_H__
+#define __APPLICATION_TILE_H__
+
+#include "nameplate-tile.h"
+
+#include <libgnome/gnome-desktop-item.h>
+
+G_BEGIN_DECLS
+
+#define APPLICATION_TILE_TYPE         (application_tile_get_type ())
+#define APPLICATION_TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), APPLICATION_TILE_TYPE, ApplicationTile))
+#define APPLICATION_TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), APPLICATION_TILE_TYPE, ApplicationTileClass))
+#define IS_APPLICATION_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), APPLICATION_TILE_TYPE))
+#define IS_APPLICATION_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), APPLICATION_TILE_TYPE))
+#define APPLICATION_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), APPLICATION_TILE_TYPE, ApplicationTileClass))
+#define APPLICATION_TILE_ACTION_START             0
+#define APPLICATION_TILE_ACTION_HELP              1
+#define APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU  2
+#define APPLICATION_TILE_ACTION_UPDATE_STARTUP    3
+#define APPLICATION_TILE_ACTION_UPGRADE_PACKAGE   4
+#define APPLICATION_TILE_ACTION_UNINSTALL_PACKAGE 5
+
+typedef struct
+{
+	NameplateTile nameplate_tile;
+
+	gchar *name;
+	gchar *description;
+	gchar *gconf_prefix;
+} ApplicationTile;
+
+typedef struct
+{
+	NameplateTileClass nameplate_tile_class;
+} ApplicationTileClass;
+
+GType application_tile_get_type (void);
+
+GtkWidget *application_tile_new (const gchar * desktop_item_id);
+GtkWidget *application_tile_new_full (const gchar * desktop_item_id,
+	GtkIconSize icon_size, gboolean show_generic_name, const gchar *gconf_prefix);
+
+GnomeDesktopItem *application_tile_get_desktop_item (ApplicationTile * tile);
+
+G_END_DECLS
+#endif
diff --git a/libslab/bookmark-agent.c b/libslab/bookmark-agent.c
new file mode 100644
index 0000000..9bd4bd2
--- /dev/null
+++ b/libslab/bookmark-agent.c
@@ -0,0 +1,1238 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2007 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "bookmark-agent.h"
+
+#ifdef HAVE_CONFIG_H
+#	include <config.h>
+#else
+#	define PACKAGE "gnome-main-menu"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include "libslab-utils.h"
+
+#define MODIFIABLE_APPS_GCONF_KEY "/desktop/gnome/applications/main-menu/lock-down/user_modifiable_apps"
+#define MODIFIABLE_DOCS_GCONF_KEY "/desktop/gnome/applications/main-menu/lock-down/user_modifiable_docs"
+#define MODIFIABLE_DIRS_GCONF_KEY "/desktop/gnome/applications/main-menu/lock-down/user_modifiable_dirs"
+#define MODIFIABLE_SYS_GCONF_KEY  "/desktop/gnome/applications/main-menu/lock-down/user_modifiable_system_area"
+
+#define USER_APPS_STORE_FILE_NAME "applications.xbel"
+#define USER_DOCS_STORE_FILE_NAME "documents.xbel"
+#define USER_DIRS_STORE_FILE_NAME "places.xbel"
+#define SYSTEM_STORE_FILE_NAME    "system-items.xbel"
+#define CALC_TEMPLATE_FILE_NAME   "empty.ots"
+#define WRITER_TEMPLATE_FILE_NAME "empty.ott"
+
+#define GTK_BOOKMARKS_FILE ".gtk-bookmarks"
+
+#define TYPE_IS_RECENT(type) ((type) == BOOKMARK_STORE_RECENT_APPS || (type) == BOOKMARK_STORE_RECENT_DOCS)
+
+typedef struct {
+	BookmarkStoreType        type;
+
+	BookmarkItem           **items;
+	gint                     n_items;
+	BookmarkStoreStatus      status;
+
+	GBookmarkFile           *store;
+	gboolean                 needs_sync;
+
+	gchar                   *store_path;
+	gchar                   *user_store_path;
+	gboolean                 user_modifiable;
+	gboolean                 reorderable;
+	const gchar             *store_filename;
+	const gchar             *lockdown_key;
+
+	GFileMonitor            *store_monitor;
+	GFileMonitor            *user_store_monitor;
+	guint                    gconf_monitor;
+
+	void                  (* update_path) (BookmarkAgent *);
+	void                  (* load_store)  (BookmarkAgent *);
+	void                  (* save_store)  (BookmarkAgent *);
+	void                  (* create_item) (BookmarkAgent *, const gchar *);
+
+	gchar                   *gtk_store_path;
+	GFileMonitor            *gtk_store_monitor;
+} BookmarkAgentPrivate;
+
+#define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BOOKMARK_AGENT_TYPE, BookmarkAgentPrivate))
+
+enum {
+	PROP_0,
+	PROP_ITEMS,
+	PROP_STATUS
+};
+
+static BookmarkAgent *instances [BOOKMARK_STORE_N_TYPES];
+
+static BookmarkAgentClass *bookmark_agent_parent_class = NULL;
+
+static void           bookmark_agent_base_init  (BookmarkAgentClass *);
+static void           bookmark_agent_class_init (BookmarkAgentClass *);
+static void           bookmark_agent_init       (BookmarkAgent      *);
+static BookmarkAgent *bookmark_agent_new        (BookmarkStoreType   );
+
+static void get_property (GObject *, guint, GValue *, GParamSpec *);
+static void set_property (GObject *, guint, const GValue *, GParamSpec *);
+static void finalize     (GObject *);
+
+static void update_agent (BookmarkAgent *);
+static void update_items (BookmarkAgent *);
+static void save_store   (BookmarkAgent *);
+static gint get_rank     (BookmarkAgent *, const gchar *);
+static void set_rank     (BookmarkAgent *, const gchar *, gint);
+
+static void load_xbel_store          (BookmarkAgent *);
+static void load_places_store        (BookmarkAgent *);
+static void update_user_spec_path    (BookmarkAgent *);
+static void save_xbel_store          (BookmarkAgent *);
+static void create_app_item          (BookmarkAgent *, const gchar *);
+static void create_doc_item          (BookmarkAgent *, const gchar *);
+static void create_dir_item          (BookmarkAgent *, const gchar *);
+
+static void store_monitor_cb (GFileMonitor *, GFile *, GFile *,
+                              GFileMonitorEvent, gpointer);
+static void gconf_notify_cb  (GConfClient *, guint, GConfEntry *, gpointer);
+static void weak_destroy_cb  (gpointer, GObject *);
+
+static gint recent_item_mru_comp_func (gconstpointer a, gconstpointer b);
+
+static gchar *find_package_data_file (const gchar *filename);
+
+
+GType
+bookmark_agent_get_type ()
+{
+	static GType g_define_type_id = 0;
+
+	if (G_UNLIKELY (g_define_type_id == 0)) {
+		static const GTypeInfo info = {
+			sizeof (BookmarkAgentClass),
+			(GBaseInitFunc) bookmark_agent_base_init,
+			NULL,
+			(GClassInitFunc) bookmark_agent_class_init,
+			NULL, NULL,
+			sizeof (BookmarkAgent), 0,
+			(GInstanceInitFunc) bookmark_agent_init,
+			NULL
+		};
+
+		g_define_type_id = g_type_register_static (
+			G_TYPE_OBJECT, "BookmarkAgent", & info, 0);
+	}
+
+	return g_define_type_id;
+}
+
+BookmarkAgent *
+bookmark_agent_get_instance (BookmarkStoreType type)
+{
+	g_return_val_if_fail (0 <= type, NULL);
+	g_return_val_if_fail (type < BOOKMARK_STORE_N_TYPES, NULL);
+
+	if (! instances [type]) {
+		instances [type] = bookmark_agent_new (type);
+		g_object_weak_ref (G_OBJECT (instances [type]), weak_destroy_cb, GINT_TO_POINTER (type));
+	}
+	else
+		g_object_ref (G_OBJECT (instances [type]));
+
+	return instances [type];
+}
+
+gboolean
+bookmark_agent_has_item (BookmarkAgent *this, const gchar *uri)
+{
+	return g_bookmark_file_has_item (PRIVATE (this)->store, uri);
+}
+
+void
+bookmark_agent_add_item (BookmarkAgent *this, const BookmarkItem *item)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	if (! item)
+		return;
+
+	g_return_if_fail (priv->user_modifiable);
+	g_return_if_fail (item->uri);
+	g_return_if_fail (item->mime_type);
+
+	g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
+
+	if (item->mtime)
+		g_bookmark_file_set_modified (priv->store, item->uri, item->mtime);
+
+	if (item->title)
+		g_bookmark_file_set_title (priv->store, item->uri, item->title);
+
+	g_bookmark_file_add_application (priv->store, item->uri, item->app_name, item->app_exec);
+
+	set_rank (this, item->uri, g_bookmark_file_get_size (priv->store) - 1);
+
+	save_store (this);
+}
+
+void
+bookmark_agent_move_item (BookmarkAgent *this, const gchar *uri, const gchar *uri_new)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	GError *error = NULL;
+
+	if (! TYPE_IS_RECENT (priv->type))
+		return;
+
+	gtk_recent_manager_move_item (
+		gtk_recent_manager_get_default (), uri, uri_new, & error);
+
+	if (error)
+		libslab_handle_g_error (
+			& error, "%s: unable to update %s with renamed file, [%s] -> [%s].",
+			G_STRFUNC, priv->store_path, uri, uri_new);
+}
+
+void
+bookmark_agent_remove_item (BookmarkAgent *this, const gchar *uri)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gint rank;
+
+	GError *error = NULL;
+
+	gchar **uris = NULL;
+	gint    rank_i;
+	gint    i;
+
+
+	g_return_if_fail (priv->user_modifiable);
+
+	if (! bookmark_agent_has_item (this, uri))
+		return;
+
+	if (TYPE_IS_RECENT (priv->type)) {
+		gtk_recent_manager_remove_item (
+			gtk_recent_manager_get_default (), uri, & error);
+
+		if (error)
+			libslab_handle_g_error (
+				& error, "%s: unable to remove [%s] from %s.",
+				G_STRFUNC, priv->store_path, uri);
+	}
+	else {
+		rank = get_rank (this, uri);
+
+		g_bookmark_file_remove_item (priv->store, uri, NULL);
+
+		if (rank >= 0) {
+			uris = g_bookmark_file_get_uris (priv->store, NULL);
+				 
+			for (i =  0; uris && uris [i]; ++i) {
+				rank_i = get_rank (this, uris [i]);
+
+				if (rank_i > rank)
+					set_rank (this, uris [i], rank_i - 1);
+			}
+
+			g_strfreev (uris);
+		}
+
+		save_store (this);
+	}
+}
+
+void
+bookmark_agent_reorder_items (BookmarkAgent *this, const gchar **uris)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gint i;
+
+
+	g_return_if_fail (priv->reorderable);
+
+	for (i = 0; uris && uris [i]; ++i)
+		set_rank (this, uris [i], i);
+
+	save_store (this);
+}
+
+static GList *
+make_items_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+	gchar **uris;
+	gint i;
+	GList *items_ordered;
+
+	if (!store)
+		return NULL;
+
+	uris = g_bookmark_file_get_uris (store, NULL);
+	items_ordered = NULL;
+
+	for (i = 0; uris && uris [i]; ++i) {
+		gboolean include;
+
+		if (priv->type == BOOKMARK_STORE_RECENT_APPS)
+			include = g_bookmark_file_has_group (store, uris [i], "recently-used-apps", NULL);
+		else
+			include = ! g_bookmark_file_get_is_private (store, uris [i], NULL);
+
+		if (include) {
+			BookmarkItem *item;
+
+			item = g_new0 (BookmarkItem, 1);
+
+			item->uri       = g_strdup (uris [i]);
+			item->mime_type = g_bookmark_file_get_mime_type (store, uris [i], NULL);
+			item->mtime     = g_bookmark_file_get_modified  (store, uris [i], NULL);
+
+			items_ordered = g_list_prepend (items_ordered, item);
+		}
+	}
+
+	items_ordered = g_list_sort (items_ordered, recent_item_mru_comp_func);
+
+	g_strfreev (uris);
+
+	return items_ordered;
+}
+
+void
+bookmark_agent_update_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
+{
+	BookmarkAgentPrivate *priv;
+	GList *items_ordered;
+	GList  *node;
+
+	g_return_if_fail (IS_BOOKMARK_AGENT (this));
+
+	priv = PRIVATE (this);
+
+	libslab_checkpoint ("bookmark_agent_update_from_bookmark_file(): start updating");
+
+	items_ordered = make_items_from_bookmark_file (this, store);
+
+	g_bookmark_file_free (priv->store);
+	priv->store = g_bookmark_file_new ();
+
+	for (node = items_ordered; node; node = node->next) {
+		BookmarkItem *item;
+
+		item = (BookmarkItem *) node->data;
+
+		g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
+		g_bookmark_file_set_modified  (priv->store, item->uri, item->mtime);
+
+		bookmark_item_free (item);
+	}
+
+	g_list_free (items_ordered);
+
+	libslab_checkpoint ("bookmark_agent_update_from_bookmark_file(): updating internal items");
+	update_items (this);
+
+	libslab_checkpoint ("bookmark_agent_update_from_bookmark_file(): end updating");
+}
+
+void
+bookmark_item_free (BookmarkItem *item)
+{
+	if (! item)
+		return;
+
+	g_free (item->uri);
+	g_free (item->title);
+	g_free (item->mime_type);
+	g_free (item->icon);
+	g_free (item->app_name);
+	g_free (item->app_exec);
+	g_free (item);
+}
+
+static void
+bookmark_agent_base_init (BookmarkAgentClass *this_class)
+{
+	gint i;
+
+	for (i = 0; i < BOOKMARK_STORE_N_TYPES; ++i)
+		instances [i] = NULL;
+}
+
+static void
+bookmark_agent_class_init (BookmarkAgentClass *this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+
+	GParamSpec *items_pspec;
+	GParamSpec *status_pspec;
+
+
+	g_obj_class->get_property = get_property;
+	g_obj_class->set_property = set_property;
+	g_obj_class->finalize     = finalize;
+
+	items_pspec = g_param_spec_pointer (
+		BOOKMARK_AGENT_ITEMS_PROP, BOOKMARK_AGENT_ITEMS_PROP,
+		"the null-terminated list which contains the bookmark items in this store",
+		G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+
+	status_pspec = g_param_spec_int (
+		BOOKMARK_AGENT_STORE_STATUS_PROP, BOOKMARK_AGENT_STORE_STATUS_PROP, "the status of the store",
+		BOOKMARK_STORE_DEFAULT_ONLY, BOOKMARK_STORE_USER, BOOKMARK_STORE_DEFAULT,
+		G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+
+	g_object_class_install_property (g_obj_class, PROP_ITEMS,  items_pspec);
+	g_object_class_install_property (g_obj_class, PROP_STATUS, status_pspec);
+
+	g_type_class_add_private (this_class, sizeof (BookmarkAgentPrivate));
+
+	bookmark_agent_parent_class = g_type_class_peek_parent (this_class);
+}
+
+static void
+bookmark_agent_init (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	priv->type                = -1;
+
+ 	priv->items               = NULL;
+ 	priv->n_items             = 0;
+	priv->status              = BOOKMARK_STORE_ABSENT;
+
+	priv->store               = NULL;
+	priv->needs_sync          = FALSE;
+
+	priv->store_path          = NULL;
+	priv->user_store_path     = NULL;
+	priv->user_modifiable     = FALSE;
+	priv->reorderable         = FALSE;
+	priv->store_filename      = NULL;
+	priv->lockdown_key        = NULL;
+
+	priv->store_monitor       = NULL;
+	priv->user_store_monitor  = NULL;
+	priv->gconf_monitor       = 0;
+
+	priv->update_path         = NULL;
+	priv->load_store          = NULL;
+	priv->save_store          = NULL;
+	priv->create_item         = NULL;
+
+	priv->gtk_store_path      = NULL;
+	priv->gtk_store_monitor   = NULL;
+}
+
+static BookmarkAgent *
+bookmark_agent_new (BookmarkStoreType type)
+{
+	BookmarkAgent        *this;
+	BookmarkAgentPrivate *priv;
+	GFile *gtk_store_file;
+
+	this = g_object_new (BOOKMARK_AGENT_TYPE, NULL);
+	priv = PRIVATE (this);
+
+	priv->type  = type;
+	priv->store = g_bookmark_file_new ();
+
+	switch (type) {
+		case BOOKMARK_STORE_USER_APPS:
+			priv->lockdown_key   = MODIFIABLE_APPS_GCONF_KEY;
+			priv->store_filename = USER_APPS_STORE_FILE_NAME;
+			priv->create_item    = create_app_item;
+
+			break;
+
+		case BOOKMARK_STORE_USER_DOCS:
+			priv->lockdown_key   = MODIFIABLE_DOCS_GCONF_KEY;
+			priv->store_filename = USER_DOCS_STORE_FILE_NAME;
+			priv->create_item    = create_doc_item;
+
+			break;
+
+		case BOOKMARK_STORE_USER_DIRS:
+			priv->lockdown_key   = MODIFIABLE_DIRS_GCONF_KEY;
+			priv->store_filename = USER_DIRS_STORE_FILE_NAME;
+			priv->create_item    = create_dir_item;
+
+			priv->user_modifiable = GPOINTER_TO_INT (libslab_get_gconf_value (priv->lockdown_key));
+			priv->reorderable     = FALSE;
+
+			priv->load_store = load_places_store;
+
+			priv->gtk_store_path = g_build_filename (g_get_home_dir (), GTK_BOOKMARKS_FILE, NULL);
+			gtk_store_file = g_file_new_for_path (priv->gtk_store_path);
+			priv->gtk_store_monitor = g_file_monitor_file (gtk_store_file,
+								       0, NULL, NULL);
+			if (priv->gtk_store_monitor) {
+				g_signal_connect (priv->gtk_store_monitor, "changed",
+						  G_CALLBACK (store_monitor_cb), this);
+			}
+
+			g_object_unref (gtk_store_file);
+
+			break;
+
+		case BOOKMARK_STORE_RECENT_APPS:
+		case BOOKMARK_STORE_RECENT_DOCS:
+			priv->user_modifiable = TRUE;
+			priv->reorderable     = FALSE;
+
+			priv->store_path = g_build_filename (g_get_home_dir (), ".recently-used.xbel", NULL);
+
+			break;
+
+		case BOOKMARK_STORE_SYSTEM:
+			priv->lockdown_key   = MODIFIABLE_SYS_GCONF_KEY;
+			priv->store_filename = SYSTEM_STORE_FILE_NAME;
+			priv->create_item    = create_app_item;
+
+			break;
+
+		default:
+			break;
+	}
+
+	if (
+		type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS ||
+		type == BOOKMARK_STORE_USER_DIRS || type == BOOKMARK_STORE_SYSTEM)
+	{
+		priv->user_modifiable = GPOINTER_TO_INT (libslab_get_gconf_value (priv->lockdown_key));
+
+		priv->user_store_path = g_build_filename (
+			g_get_user_data_dir (), PACKAGE, priv->store_filename, NULL);
+
+		priv->update_path = update_user_spec_path;
+
+		priv->gconf_monitor = libslab_gconf_notify_add (
+			priv->lockdown_key, gconf_notify_cb, this);
+	}
+
+	if (type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS || type == BOOKMARK_STORE_SYSTEM) {
+		priv->reorderable = TRUE;
+		priv->load_store  = load_xbel_store;
+		priv->save_store  = save_xbel_store;
+	}
+
+	update_agent (this);
+
+	return this;
+}
+
+static void
+get_property (GObject *g_obj, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	BookmarkAgent        *this = BOOKMARK_AGENT (g_obj);
+	BookmarkAgentPrivate *priv = PRIVATE        (this);
+
+
+	switch (prop_id) {
+		case PROP_ITEMS:
+			g_value_set_pointer (value, priv->items);
+			break;
+
+		case PROP_STATUS:
+			g_value_set_int (value, priv->status);
+			break;
+	}
+}
+
+static void
+set_property (GObject *g_obj, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	/* no writeable properties */
+}
+
+static void
+finalize (GObject *g_obj)
+{
+	BookmarkAgent *this = BOOKMARK_AGENT (g_obj);
+	BookmarkAgentPrivate *priv = PRIVATE (g_obj);
+
+	gint i;
+
+
+	for (i = 0; priv->items && priv->items [i]; ++i)
+		bookmark_item_free (priv->items [i]);
+
+	g_free (priv->items);
+	g_free (priv->store_path);
+	g_free (priv->user_store_path);
+	g_free (priv->gtk_store_path);
+
+	if (priv->store_monitor) {
+		g_signal_handlers_disconnect_by_func (priv->store_monitor, store_monitor_cb, this);
+		g_file_monitor_cancel (priv->store_monitor);
+		g_object_unref (priv->store_monitor);
+	}
+
+	if (priv->user_store_monitor) {
+		g_signal_handlers_disconnect_by_func (priv->user_store_monitor, store_monitor_cb, this);
+		g_file_monitor_cancel (priv->user_store_monitor);
+		g_object_unref (priv->user_store_monitor);
+	}
+
+	if (priv->gtk_store_monitor) {
+		g_signal_handlers_disconnect_by_func (priv->gtk_store_monitor, store_monitor_cb, this);
+		g_file_monitor_cancel (priv->gtk_store_monitor);
+		g_object_unref (priv->gtk_store_monitor);
+	}
+
+	libslab_gconf_notify_remove (priv->gconf_monitor);
+
+	g_bookmark_file_free (priv->store);
+
+	G_OBJECT_CLASS (bookmark_agent_parent_class)->finalize (g_obj);
+}
+
+static void
+update_agent (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	if (priv->update_path)
+		priv->update_path (this);
+
+	if (priv->load_store)
+		priv->load_store (this);
+
+	update_items (this);
+}
+
+static void
+update_items (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar    **uris            = NULL;
+	gchar    **uris_ordered    = NULL;
+	gsize      n_uris          = 0;
+	gint       rank            = -1;
+	gint       rank_corr       = -1;
+	gboolean   needs_update    = FALSE;
+	gboolean   store_corrupted = FALSE;
+	gchar     *new_title, *old_title;
+
+	gint i;
+
+
+	uris = g_bookmark_file_get_uris (priv->store, & n_uris);
+	uris_ordered = g_new0 (gchar *, n_uris + 1);
+	uris_ordered [n_uris] = NULL;
+
+	for (i = 0; uris && uris [i]; ++i) {
+		rank = get_rank (this, uris [i]);
+
+		if (rank < 0 || rank >= n_uris)
+			rank = i;
+
+		if (uris_ordered [rank]) {
+			store_corrupted = TRUE;
+			rank_corr = rank;
+
+			for (rank = 0; rank < n_uris; ++rank)
+				if (! uris_ordered [rank])
+					break;
+
+			g_warning (
+				"store corruption [%s] - multiple uris with same rank (%d): [%s] [%s], moving latter to %d",
+				priv->store_path, rank_corr, uris_ordered [rank_corr], uris [i], rank);
+		}
+
+		set_rank (this, uris [i], rank);
+
+		uris_ordered [rank] = uris [i];
+	}
+
+	if (priv->n_items != n_uris)
+		needs_update = TRUE;
+
+	for (i = 0; ! needs_update && uris_ordered && uris_ordered [i]; ++i) {
+		if (priv->type == BOOKMARK_STORE_USER_DIRS) {
+			new_title = g_bookmark_file_get_title (priv->store, uris_ordered [i], NULL);
+			old_title = priv->items [i]->title;
+			if (!new_title && !old_title) {
+				if (strcmp (priv->items [i]->uri, uris_ordered [i]))
+					needs_update = TRUE;
+			}
+			else if ((new_title && !old_title) || (!new_title && old_title))
+				needs_update = TRUE;
+			else if (strcmp (old_title, new_title))
+				needs_update = TRUE;
+			g_free (new_title);
+		}
+		else if (strcmp (priv->items [i]->uri, uris_ordered [i]))
+			needs_update = TRUE;
+	}
+
+	if (needs_update) {
+		for (i = 0; priv->items && priv->items [i]; ++i)
+			bookmark_item_free (priv->items [i]);
+
+		g_free (priv->items);
+
+		priv->n_items = n_uris;
+		priv->items = g_new0 (BookmarkItem *, priv->n_items + 1);
+
+		for (i = 0; uris_ordered && uris_ordered [i]; ++i) {
+			priv->items [i]            = g_new0 (BookmarkItem, 1);
+			priv->items [i]->uri       = g_strdup (uris_ordered [i]);
+			priv->items [i]->title     = g_bookmark_file_get_title     (priv->store, uris_ordered [i], NULL);
+			priv->items [i]->mime_type = g_bookmark_file_get_mime_type (priv->store, uris_ordered [i], NULL);
+			priv->items [i]->mtime     = g_bookmark_file_get_modified  (priv->store, uris_ordered [i], NULL);
+			priv->items [i]->app_name  = NULL;
+			priv->items [i]->app_exec  = NULL;
+
+			g_bookmark_file_get_icon (priv->store, uris_ordered [i], & priv->items [i]->icon, NULL, NULL);
+		}
+
+		/* Since the bookmark store for recently-used items is updated by the caller of BookmarkAgent,
+		 * we don't emit notifications in that case.  The caller will know when to update itself.
+		 */
+		if (!TYPE_IS_RECENT (priv->type))
+			g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_ITEMS_PROP);
+	}
+
+	if (store_corrupted)
+		save_store (this);
+
+	g_strfreev (uris);
+	g_free (uris_ordered);
+}
+
+static void
+save_store (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar *dir;
+
+
+	g_return_if_fail (priv->user_modifiable);
+
+	priv->needs_sync = TRUE;
+	priv->update_path (this);
+
+	dir = g_path_get_dirname (priv->store_path);
+	g_mkdir_with_parents (dir, 0700);
+	g_free (dir);
+
+	priv->save_store (this);
+	update_items (this);
+}
+
+static gint
+get_rank (BookmarkAgent *this, const gchar *uri)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar **groups;
+	gint    rank;
+
+	gint i;
+
+
+	if (! priv->reorderable)
+		return -1;
+
+	groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
+	rank   = -1;
+
+	for (i = 0; groups && groups [i]; ++i) {
+		if (g_str_has_prefix (groups [i], "rank-")) {
+			if (rank >= 0)
+				g_warning (
+					"store corruption - multiple ranks for same uri: [%s] [%s]",
+					priv->store_path, uri);
+
+			rank = atoi (& groups [i] [5]);
+		}
+	}
+
+	g_strfreev (groups);
+
+	return rank;
+}
+
+static void
+set_rank (BookmarkAgent *this, const gchar *uri, gint rank)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar **groups;
+	gchar  *group;
+
+	gint i;
+
+
+	if (! (priv->reorderable && bookmark_agent_has_item (this, uri)))
+		return;
+
+	groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
+
+	for (i = 0; groups && groups [i]; ++i)
+		if (g_str_has_prefix (groups [i], "rank-"))
+			g_bookmark_file_remove_group (priv->store, uri, groups [i], NULL);
+
+	g_strfreev (groups);
+
+	group = g_strdup_printf ("rank-%d", rank);
+	g_bookmark_file_add_group (priv->store, uri, group);
+	g_free (group);
+}
+
+static void
+load_xbel_store (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar **uris = NULL;
+
+	GError *error = NULL;
+
+	gint i;
+	gboolean success;
+
+	if (!priv->store_path)
+		success = FALSE;
+	else {
+		libslab_checkpoint ("load_xbel_store(): start loading %s", priv->store_path);
+		success = g_bookmark_file_load_from_file (priv->store, priv->store_path, & error);
+		libslab_checkpoint ("load_xbel_store(): end loading %s", priv->store_path);
+	}
+
+	if (!success) {
+		g_bookmark_file_free (priv->store);
+		priv->store = g_bookmark_file_new ();
+
+		libslab_handle_g_error (
+			& error, "%s: couldn't load bookmark file [%s]\n",
+			G_STRFUNC, priv->store_path ? priv->store_path : "NULL");
+
+		return;
+	}
+
+	libslab_checkpoint ("load_xbel_store(): start creating items from %s", priv->store_path);
+
+	uris = g_bookmark_file_get_uris (priv->store, NULL);
+
+	for (i = 0; uris && uris [i]; ++i)
+		priv->create_item (this, uris [i]);
+
+	g_strfreev (uris);
+
+	libslab_checkpoint ("load_xbel_store(): end creating items from %s", priv->store_path);
+}
+
+static void
+load_places_store (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar **uris;
+	gchar **groups;
+	gchar **bookmarks = NULL;
+	
+	gchar  *buf, *label, *uri;
+
+	gint i, j, bookmark_len;
+
+	load_xbel_store (this);
+
+	uris = g_bookmark_file_get_uris (priv->store, NULL);
+
+	for (i = 0; uris && uris [i]; ++i) {
+		groups = g_bookmark_file_get_groups (priv->store, uris [i], NULL, NULL);
+
+		for (j = 0; groups && groups [j]; ++j) {
+			if (! strcmp (groups [j], "gtk-bookmarks")) {
+				g_bookmark_file_remove_item (priv->store, uris [i], NULL);
+
+				break;
+			}
+		}
+
+		g_strfreev (groups);
+	}
+
+	g_strfreev (uris);
+
+	g_file_get_contents (priv->gtk_store_path, & buf, NULL, NULL);
+
+	if (buf) {
+		bookmarks = g_strsplit (buf, "\n", -1);
+		g_free (buf);
+	}
+
+	for (i = 0; bookmarks && bookmarks [i]; ++i) {
+		bookmark_len = strlen (bookmarks [i]);
+		if (bookmark_len > 0) {
+			label = strstr (bookmarks[i], " ");
+			if (label != NULL)
+				uri = g_strndup (bookmarks [i], bookmark_len - strlen (label));
+			else
+				uri = bookmarks [i];
+			g_bookmark_file_add_group (priv->store, uri, "gtk-bookmarks");
+			priv->create_item (this, uri);
+			if (label != NULL) {
+				label++;
+				if (strlen (label) > 0)
+					g_bookmark_file_set_title (priv->store, uri, label);
+				g_free (uri);
+			}
+		}
+	}
+
+	g_strfreev (bookmarks);
+}
+
+static gchar *
+find_package_data_file (const gchar *filename)
+{
+	const gchar * const *dirs = NULL;
+	gchar               *path = NULL;
+	gint                 i;
+
+
+	dirs = g_get_system_data_dirs ();
+	
+	for (i = 0; ! path && dirs && dirs [i]; ++i) {
+		path = g_build_filename (dirs [i], PACKAGE, filename, NULL);
+		
+		if (! g_file_test (path, G_FILE_TEST_EXISTS)) {
+			g_free (path);
+			path = NULL;
+		}
+	}
+
+	return path;
+}
+
+static void
+update_user_spec_path (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gboolean  use_user_path;
+	gchar    *path = NULL;
+
+	BookmarkStoreStatus status;
+
+	use_user_path = priv->user_modifiable &&
+		(priv->needs_sync || g_file_test (priv->user_store_path, G_FILE_TEST_EXISTS));
+
+	if (use_user_path)
+		path = g_strdup (priv->user_store_path);
+	else
+		path = find_package_data_file (priv->store_filename);
+
+	if (use_user_path)
+		status = BOOKMARK_STORE_USER;
+	else if (path && priv->user_modifiable)
+		status = BOOKMARK_STORE_DEFAULT;
+	else if (path)
+		status = BOOKMARK_STORE_DEFAULT_ONLY;
+	else
+		status = BOOKMARK_STORE_ABSENT;
+	
+	if (priv->status != status) {
+		priv->status = status;
+		g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_STORE_STATUS_PROP);
+
+		if (priv->user_store_monitor) {
+			g_file_monitor_cancel (priv->user_store_monitor);
+			g_object_unref (priv->user_store_monitor);
+			priv->user_store_monitor = NULL;
+		}
+
+		if (priv->status == BOOKMARK_STORE_DEFAULT) {
+			GFile *user_store_file;
+
+			user_store_file = g_file_new_for_path (priv->user_store_path);
+			priv->user_store_monitor = g_file_monitor_file (user_store_file,
+									0, NULL, NULL);
+			if (priv->user_store_monitor) {
+				g_signal_connect (priv->user_store_monitor, "changed",
+						  G_CALLBACK (store_monitor_cb), this);
+			}
+
+			g_object_unref (user_store_file);
+		}
+	}
+
+	if (libslab_strcmp (priv->store_path, path)) {
+		g_free (priv->store_path);
+		priv->store_path = path;
+
+		if (priv->store_monitor) {
+			g_file_monitor_cancel (priv->store_monitor);
+			g_object_unref (priv->store_monitor);
+		}
+
+		if (priv->store_path) {
+			GFile *store_file;
+
+			store_file = g_file_new_for_path (priv->store_path);
+			priv->store_monitor = g_file_monitor_file (store_file,
+								   0, NULL, NULL);
+			if (priv->store_monitor) {
+				g_signal_connect (priv->store_monitor, "changed",
+						  G_CALLBACK (store_monitor_cb), this);
+			}
+
+			g_object_unref (store_file);
+		}
+	}
+	else
+		g_free (path);
+}
+
+static void
+save_xbel_store (BookmarkAgent *this)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	GError *error = NULL;
+
+
+	if (! g_bookmark_file_to_file (priv->store, priv->store_path, & error))
+		libslab_handle_g_error (
+			& error, "%s: couldn't save bookmark file [%s]\n", G_STRFUNC, priv->store_path);
+}
+
+static void
+create_app_item (BookmarkAgent *this, const gchar *uri)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	GnomeDesktopItem *ditem;
+	gchar *uri_new = NULL;
+
+	ditem = libslab_gnome_desktop_item_new_from_unknown_id (uri);
+
+	if (ditem) {
+		uri_new = g_strdup (gnome_desktop_item_get_location (ditem));
+		gnome_desktop_item_unref (ditem);
+	}
+
+	if (! uri_new)
+		return;
+
+	if (libslab_strcmp (uri, uri_new))
+		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
+
+	g_free (uri_new);
+}
+
+static void
+create_doc_item (BookmarkAgent *this, const gchar *uri)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar *uri_new = NULL;
+	gchar *path;
+	gchar *dir;
+	gchar *file;
+	gchar *template = NULL;
+	gsize  length;
+	gchar *contents;
+
+
+	if (! (strcmp (uri, "BLANK_SPREADSHEET") && strcmp (uri, "BLANK_DOCUMENT"))) {
+		dir = g_build_filename (g_get_home_dir (), "Documents", NULL);
+
+		if (! strcmp (uri, "BLANK_SPREADSHEET")) {
+			g_bookmark_file_set_title (priv->store, uri, "BLANK_SPREADSHEET");
+			file = g_strconcat (_("New Spreadsheet"), ".ots", NULL);
+			template = find_package_data_file (CALC_TEMPLATE_FILE_NAME);
+		}
+		else {
+			g_bookmark_file_set_title (priv->store, uri, "BLANK_DOCUMENT");
+			file = g_strconcat (_("New Document"), ".ott", NULL);
+			template = find_package_data_file (WRITER_TEMPLATE_FILE_NAME);
+		}
+
+		path = g_build_filename (dir, file, NULL);
+
+		if (! g_file_test (path, G_FILE_TEST_EXISTS)) {
+			g_mkdir_with_parents (dir, 0700);
+
+			if (template != NULL) {
+				if (g_file_get_contents (template, & contents, & length, NULL))
+					g_file_set_contents (path, contents, length, NULL);
+
+				g_free (contents);
+			}
+			else
+				fclose (g_fopen (path, "w"));
+		}
+
+		uri_new = g_filename_to_uri (path, NULL, NULL);
+
+		g_free (dir);
+		g_free (file);
+		g_free (path);
+		g_free (template);
+	}
+
+	if (! uri_new)
+		return;
+
+	if (libslab_strcmp (uri, uri_new))
+		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
+
+	g_free (uri_new);
+}
+
+static void
+create_dir_item (BookmarkAgent *this, const gchar *uri)
+{
+	BookmarkAgentPrivate *priv = PRIVATE (this);
+
+	gchar *uri_new = NULL;
+	gchar *path;
+	gchar *name = NULL;
+	gchar *icon = NULL;
+
+	gchar *buf;
+	gchar *tag_open_ptr  = NULL;
+	gchar *tag_close_ptr = NULL;
+	gchar *search_string = NULL;
+
+	if (! strcmp (uri, "HOME")) {
+		uri_new = g_filename_to_uri (g_get_home_dir (), NULL, NULL);
+		name    = _("Home");
+		icon    = "gnome-fs-home";
+	}
+	else if (! strcmp (uri, "DOCUMENTS")) {
+		path = g_build_filename (g_get_home_dir (), "Documents", NULL);
+		name = _("Documents");
+		uri_new = g_filename_to_uri (path, NULL, NULL);
+		g_free (path);
+	}
+	else if (! strcmp (uri, "DESKTOP")) {
+		path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
+		name = _("Desktop");
+		uri_new = g_filename_to_uri (path, NULL, NULL);
+		icon = "gnome-fs-desktop";
+		g_free (path);
+	}
+	else if (! strcmp (uri, "file:///")) {
+		icon = "drive-harddisk";
+		name = _("File System");
+	}
+	else if (! strcmp (uri, "network:")) {
+		icon = "network-workgroup";
+		name = _("Network Servers");
+	}
+	else if (g_str_has_prefix (uri, "x-nautilus-search")) {
+		icon = "system-search";
+
+		path = g_build_filename (g_get_home_dir (), ".nautilus", "searches", & uri [21], NULL);
+
+		if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+			g_file_get_contents (path, & buf, NULL, NULL);
+
+			if (buf) {
+				tag_open_ptr  = strstr (buf, "<text>");
+				tag_close_ptr = strstr (buf, "</text>");
+			}
+
+			if (tag_open_ptr && tag_close_ptr) {
+				tag_close_ptr [0] = '\0';
+
+				search_string = g_strdup_printf ("\"%s\"", & tag_open_ptr [6]);
+
+				tag_close_ptr [0] = 'a';
+			}
+
+			g_free (buf);
+		}
+
+		if (search_string)
+			name = search_string;
+		else
+			name = _("Search");
+
+		g_free (path);
+	}
+
+	if (icon)
+		g_bookmark_file_set_icon (priv->store, uri, icon, "image/png");
+
+	if (name)
+		g_bookmark_file_set_title (priv->store, uri, name);
+	
+	if (uri_new && libslab_strcmp (uri, uri_new))
+		g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
+
+	g_free (uri_new);
+}
+
+static void
+store_monitor_cb (GFileMonitor *mon, GFile *f1, GFile *f2,
+                  GFileMonitorEvent event_type, gpointer user_data)
+{
+	update_agent (BOOKMARK_AGENT (user_data));
+}
+
+static void
+gconf_notify_cb (GConfClient *client, guint conn_id,
+                 GConfEntry *entry, gpointer user_data)
+{
+	BookmarkAgent        *this = BOOKMARK_AGENT (user_data);
+	BookmarkAgentPrivate *priv = PRIVATE        (this);
+
+	gboolean user_modifiable;
+
+
+	user_modifiable = GPOINTER_TO_INT (libslab_get_gconf_value (priv->lockdown_key));
+
+	if (priv->user_modifiable != user_modifiable) {
+		priv->user_modifiable = user_modifiable;
+		update_agent (this);
+	}
+}
+
+static void
+weak_destroy_cb (gpointer data, GObject *g_obj)
+{
+	instances [GPOINTER_TO_INT (data)] = NULL;
+}
+
+static gint
+recent_item_mru_comp_func (gconstpointer a, gconstpointer b)
+{
+	return ((BookmarkItem *) b)->mtime - ((BookmarkItem *) a)->mtime;
+}
diff --git a/libslab/bookmark-agent.h b/libslab/bookmark-agent.h
new file mode 100644
index 0000000..391389e
--- /dev/null
+++ b/libslab/bookmark-agent.h
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2007 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __BOOKMARK_AGENT_H__
+#define __BOOKMARK_AGENT_H__
+
+#include <time.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define BOOKMARK_AGENT_TYPE         (bookmark_agent_get_type ())
+#define BOOKMARK_AGENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), BOOKMARK_AGENT_TYPE, BookmarkAgent))
+#define BOOKMARK_AGENT_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), BOOKMARK_AGENT_TYPE, BookmarkAgentClass))
+#define IS_BOOKMARK_AGENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), BOOKMARK_AGENT_TYPE))
+#define IS_BOOKMARK_AGENT_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), BOOKMARK_AGENT_TYPE))
+#define BOOKMARK_AGENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BOOKMARK_AGENT_TYPE, BookmarkAgentClass))
+
+#define BOOKMARK_AGENT_STORE_STATUS_PROP "store-status"
+#define BOOKMARK_AGENT_ITEMS_PROP        "items"
+
+typedef struct {
+	gchar  *uri;
+	gchar  *title;
+	gchar  *mime_type;
+	time_t  mtime;
+	gchar  *icon;
+	gchar  *app_name;
+	gchar  *app_exec;
+} BookmarkItem;
+
+typedef enum {
+	BOOKMARK_STORE_DEFAULT_ONLY,
+	BOOKMARK_STORE_DEFAULT,
+	BOOKMARK_STORE_USER,
+	BOOKMARK_STORE_ABSENT
+} BookmarkStoreStatus;
+
+typedef enum {
+	BOOKMARK_STORE_USER_APPS   = 0,
+	BOOKMARK_STORE_USER_DOCS   = 1,
+	BOOKMARK_STORE_USER_DIRS   = 2,
+	BOOKMARK_STORE_RECENT_APPS = 3,
+	BOOKMARK_STORE_RECENT_DOCS = 4,
+	BOOKMARK_STORE_SYSTEM      = 5,
+	BOOKMARK_STORE_N_TYPES     = 6
+} BookmarkStoreType;
+
+typedef struct {
+	GObject g_object;
+} BookmarkAgent;
+
+typedef struct {
+	GObjectClass g_object_class;
+} BookmarkAgentClass;
+
+GType bookmark_agent_get_type (void);
+
+BookmarkAgent *bookmark_agent_get_instance  (BookmarkStoreType type);
+gboolean       bookmark_agent_has_item      (BookmarkAgent *this, const gchar *uri);
+void           bookmark_agent_add_item      (BookmarkAgent *this, const BookmarkItem *item);
+void           bookmark_agent_move_item     (BookmarkAgent *this, const gchar *uri, const gchar *uri_new);
+void           bookmark_agent_remove_item   (BookmarkAgent *this, const gchar *uri);
+void           bookmark_agent_reorder_items (BookmarkAgent *this, const gchar **uris);
+
+void bookmark_agent_update_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store);
+
+void           bookmark_item_free           (BookmarkItem *item);
+
+G_END_DECLS
+
+#endif
diff --git a/libslab/directory-tile.c b/libslab/directory-tile.c
new file mode 100644
index 0000000..3bd3dc5
--- /dev/null
+++ b/libslab/directory-tile.c
@@ -0,0 +1,669 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "directory-tile.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include <libgnomeui/gnome-icon-lookup.h>
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+#include <unistd.h>
+
+#include "slab-gnome-util.h"
+#include "gnome-utils.h"
+#include "libslab-utils.h"
+
+#define GCONF_SEND_TO_CMD_KEY       "/desktop/gnome/applications/main-menu/file-area/file_send_to_cmd"
+#define GCONF_ENABLE_DELETE_KEY_DIR "/apps/nautilus/preferences"
+#define GCONF_ENABLE_DELETE_KEY     GCONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
+#define GCONF_CONFIRM_DELETE_KEY    GCONF_ENABLE_DELETE_KEY_DIR "/confirm_trash"
+
+G_DEFINE_TYPE (DirectoryTile, directory_tile, NAMEPLATE_TILE_TYPE)
+
+static void directory_tile_finalize (GObject *);
+static void directory_tile_style_set (GtkWidget *, GtkStyle *);
+
+static void directory_tile_private_setup (DirectoryTile *);
+static void load_image (DirectoryTile *);
+
+static GtkWidget *create_header (const gchar *);
+
+static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer);
+
+static void open_with_default_trigger (Tile *, TileEvent *, TileAction *);
+static void rename_trigger (Tile *, TileEvent *, TileAction *);
+static void move_to_trash_trigger (Tile *, TileEvent *, TileAction *);
+static void delete_trigger (Tile *, TileEvent *, TileAction *);
+static void send_to_trigger (Tile *, TileEvent *, TileAction *);
+
+static void rename_entry_activate_cb (GtkEntry *, gpointer);
+static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer);
+static void gconf_enable_delete_cb (GConfClient *, guint, GConfEntry *, gpointer);
+
+static void disown_spawned_child (gpointer);
+
+typedef struct
+{
+	gchar *basename;
+	gchar *mime_type;
+	gchar *icon_name;
+	
+	GtkBin *header_bin;
+	GAppInfo *default_app;
+	
+	gboolean image_is_broken;
+	
+	gboolean delete_enabled;
+	guint gconf_conn_id;
+} DirectoryTilePrivate;
+
+#define DIRECTORY_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DIRECTORY_TILE_TYPE, DirectoryTilePrivate))
+
+static void directory_tile_class_init (DirectoryTileClass *this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
+
+	g_obj_class->finalize = directory_tile_finalize;
+
+	widget_class->style_set = directory_tile_style_set;
+
+	g_type_class_add_private (this_class, sizeof (DirectoryTilePrivate));
+}
+
+GtkWidget *
+directory_tile_new (const gchar *in_uri, const gchar *title, const gchar *icon_name, const gchar *mime_type)
+{
+	DirectoryTile *this;
+	DirectoryTilePrivate *priv;
+
+	gchar *uri;
+	GtkWidget *image;
+	GtkWidget *header;
+	GtkMenu *context_menu;
+
+	GtkContainer *menu_ctnr;
+	GtkWidget *menu_item;
+
+	TileAction *action;
+
+	gchar *basename;
+
+	gchar *markup;
+
+	AtkObject *accessible;
+	
+	gchar *filename;
+	gchar *tooltip_text;
+
+  
+	uri = g_strdup (in_uri);
+
+	image = gtk_image_new ();
+
+	if (! title) {
+		markup = g_path_get_basename (uri);
+		basename = g_uri_unescape_string (markup, NULL);
+		g_free (markup);
+	}
+	else
+		basename = g_strdup (title);
+
+	header = create_header (basename);
+
+	filename = g_filename_from_uri (uri, NULL, NULL);
+
+  	if (filename)
+		tooltip_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+	else
+		tooltip_text = NULL;
+
+	g_free (filename);
+	
+	context_menu = GTK_MENU (gtk_menu_new ());
+
+	this = g_object_new (
+		DIRECTORY_TILE_TYPE,
+		"tile-uri",          uri,
+		"nameplate-image",   image,
+		"nameplate-header",  header,
+		"context-menu",      context_menu,
+		NULL);
+	gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text);
+
+	g_free (uri);
+	if (tooltip_text)
+		g_free (tooltip_text);
+
+	priv = DIRECTORY_TILE_GET_PRIVATE (this);
+	priv->basename    = g_strdup (basename);
+	priv->header_bin  = GTK_BIN (header);
+	priv->icon_name   = g_strdup (icon_name);
+	priv->mime_type   = g_strdup (mime_type);
+
+	directory_tile_private_setup (this);
+
+	TILE (this)->actions = g_new0 (TileAction *, 6);
+	TILE (this)->n_actions = 6;
+
+	menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
+
+	/* make open with default action */
+
+	markup = g_markup_printf_escaped (_("<b>Open</b>"));
+	action = tile_action_new (TILE (this), open_with_default_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
+	g_free (markup);
+
+	TILE (this)->default_action = action;
+
+	menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action)));
+
+	TILE (this)->actions [DIRECTORY_TILE_ACTION_OPEN] = action;
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* insert separator */
+
+	menu_item = gtk_separator_menu_item_new ();
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make rename action */
+
+	action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0);
+	TILE (this)->actions[DIRECTORY_TILE_ACTION_RENAME] = action;
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make send to action */
+
+	/* Only allow Send To for local files, ideally this would use something
+	 * equivalent to gnome_vfs_uri_is_local, but that method will stat the file and
+	 * that can hang in some conditions. */
+
+	if (!strncmp (TILE (this)->uri, "file://", 7))
+	{
+		action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."),
+			TILE_ACTION_OPENS_NEW_WINDOW);
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	}
+	else
+	{
+		action = NULL;
+
+		menu_item = gtk_menu_item_new_with_label (_("Send To..."));
+		gtk_widget_set_sensitive (menu_item, FALSE);
+	}
+
+	TILE (this)->actions[DIRECTORY_TILE_ACTION_SEND_TO] = action;
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* insert separator */
+
+	menu_item = gtk_separator_menu_item_new ();
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make move to trash action */
+
+	action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0);
+	TILE (this)->actions[DIRECTORY_TILE_ACTION_MOVE_TO_TRASH] = action;
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make delete action */
+
+	if (priv->delete_enabled)
+	{
+		action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0);
+		TILE (this)->actions[DIRECTORY_TILE_ACTION_DELETE] = action;
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_container_add (menu_ctnr, menu_item);
+	}
+
+	gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu));
+
+	load_image (this);
+
+	accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
+	if (basename)
+	  atk_object_set_name (accessible, basename);
+
+	g_free (basename);
+
+	return GTK_WIDGET (this);
+}
+
+static void
+directory_tile_private_setup (DirectoryTile *tile)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+	GConfClient *client;
+
+	if (priv->mime_type)
+		priv->default_app = g_app_info_get_default_for_type (priv->mime_type, TRUE);
+	else
+		priv->default_app = NULL;
+
+	priv->delete_enabled =
+		(gboolean) GPOINTER_TO_INT (get_gconf_value (GCONF_ENABLE_DELETE_KEY));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_add_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
+	priv->gconf_conn_id =
+		connect_gconf_notify (GCONF_ENABLE_DELETE_KEY, gconf_enable_delete_cb, tile);
+
+	g_object_unref (client);
+}
+
+static void
+directory_tile_init (DirectoryTile *tile)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+
+	priv->default_app = NULL;
+	priv->basename = NULL;
+	priv->header_bin = NULL;
+	priv->icon_name = NULL;
+	priv->mime_type = NULL;
+	priv->image_is_broken = TRUE;
+	priv->delete_enabled = FALSE;
+	priv->gconf_conn_id = 0;
+}
+
+static void
+directory_tile_finalize (GObject *g_object)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (g_object);
+
+	GConfClient *client;
+
+	g_free (priv->basename);
+	g_free (priv->icon_name);
+	g_free (priv->mime_type);
+
+	if (priv->default_app)
+		g_object_unref (priv->default_app);
+    
+	client = gconf_client_get_default ();
+
+	gconf_client_notify_remove (client, priv->gconf_conn_id);
+	gconf_client_remove_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, NULL);
+
+	g_object_unref (client);
+
+	(* G_OBJECT_CLASS (directory_tile_parent_class)->finalize) (g_object);
+}
+
+static void
+directory_tile_style_set (GtkWidget *widget, GtkStyle *prev_style)
+{
+	load_image (DIRECTORY_TILE (widget));
+}
+
+static void
+load_image (DirectoryTile *tile)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+	gchar *icon_name;
+
+	if (priv->icon_name)
+		icon_name = priv->icon_name;
+	else
+		icon_name = "gnome-fs-directory";
+
+	priv->image_is_broken = slab_load_image (
+		GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_name);
+}
+
+static GtkWidget *
+create_header (const gchar *name)
+{
+	GtkWidget *header_bin;
+	GtkWidget *header;
+
+	header = gtk_label_new (name);
+	gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+	gtk_container_add (GTK_CONTAINER (header_bin), header);
+
+	g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb),
+		NULL);
+
+	return header_bin;
+}
+
+static void
+header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
+{
+	gtk_widget_set_size_request (widget, alloc->width, -1);
+}
+
+static void
+rename_entry_activate_cb (GtkEntry *entry, gpointer user_data)
+{
+	DirectoryTile *tile = DIRECTORY_TILE (user_data);
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+
+	GFile *src_file;
+	GFile *dst_file;
+
+	gchar *dirname;
+	gchar *dst_uri;
+	gchar *src_path;
+
+	GtkWidget *child;
+	GtkWidget *header;
+
+	gboolean res;
+	GError *error = NULL;
+
+	if (strlen (gtk_entry_get_text (entry)) < 1)
+		return;
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL);
+	dirname = g_path_get_dirname (src_path);
+	dst_uri = g_build_filename (dirname, gtk_entry_get_text (entry), NULL);
+	dst_file = g_file_new_for_uri (dst_uri);
+
+	g_free (dirname);
+	g_free (src_path);
+
+	res = g_file_move (src_file, dst_file,
+			   G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
+
+	if (res) {
+		g_free (priv->basename);
+		priv->basename = g_strdup (gtk_entry_get_text (entry));
+	}
+	else {
+		g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri, dst_uri,
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_free (dst_uri);
+	g_object_unref (src_file);
+	g_object_unref (dst_file);
+
+	header = gtk_label_new (priv->basename);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	child = gtk_bin_get_child (priv->header_bin);
+
+	if (child)
+		gtk_widget_destroy (child);
+
+	gtk_container_add (GTK_CONTAINER (priv->header_bin), header);
+
+	gtk_widget_show (header);
+}
+
+static gboolean
+rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void
+gconf_enable_delete_cb (GConfClient *client, guint conn_id, GConfEntry *entry, gpointer user_data)
+{
+	Tile *tile = TILE (user_data);
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (user_data);
+
+	GtkMenuShell *menu;
+	gboolean delete_enabled;
+
+	TileAction *action;
+	GtkWidget *menu_item;
+
+	menu = GTK_MENU_SHELL (tile->context_menu);
+
+	delete_enabled = gconf_value_get_bool (entry->value);
+
+	if (delete_enabled == priv->delete_enabled)
+		return;
+
+	priv->delete_enabled = delete_enabled;
+
+	if (priv->delete_enabled)
+	{
+		action = tile_action_new (tile, delete_trigger, _("Delete"), 0);
+		tile->actions[DIRECTORY_TILE_ACTION_DELETE] = action;
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_menu_shell_insert (menu, menu_item, 7);
+
+		gtk_widget_show_all (menu_item);
+	}
+	else
+	{
+		g_object_unref (tile->actions[DIRECTORY_TILE_ACTION_DELETE]);
+
+		tile->actions[DIRECTORY_TILE_ACTION_DELETE] = NULL;
+	}
+}
+
+static void
+rename_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+
+	GtkWidget *child;
+	GtkWidget *entry;
+
+
+	entry = gtk_entry_new ();
+	gtk_entry_set_text (GTK_ENTRY (entry), priv->basename);
+	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+
+	child = gtk_bin_get_child (priv->header_bin);
+
+	if (child)
+		gtk_widget_destroy (child);
+
+	gtk_container_add (GTK_CONTAINER (priv->header_bin), entry);
+
+	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile);
+
+	g_signal_connect (G_OBJECT (entry), "key_release_event",
+		G_CALLBACK (rename_entry_key_release_cb), NULL);
+
+	gtk_widget_show (entry);
+	gtk_widget_grab_focus (entry);
+}
+
+static void
+move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	GFile *src_file;
+	gboolean res;
+	GError *error = NULL;
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	res = g_file_trash (src_file, NULL, &error);
+	if (!res) {
+		g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri,
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (src_file);
+}
+
+static void
+delete_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	GtkDialog *confirm_dialog;
+	gint       result;
+
+	GFile *src_file;
+	gboolean res;
+	GError *error = NULL;
+
+	if (GPOINTER_TO_INT (libslab_get_gconf_value (GCONF_CONFIRM_DELETE_KEY))) {
+		confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, 
+				GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), DIRECTORY_TILE_GET_PRIVATE (tile)->basename));
+		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost."));
+							
+		gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+		gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES);
+		gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES);
+
+		result = gtk_dialog_run (confirm_dialog);
+
+		gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
+
+		if (result != GTK_RESPONSE_YES)
+			return;
+	}
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	res = g_file_delete (src_file, NULL, &error);
+
+	if (!res) {
+		g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri,
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (src_file);
+}
+
+static void
+send_to_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	gchar  *cmd;
+	gint    argc;
+	gchar **argv_parsed = NULL;
+	gchar **argv        = NULL;
+
+	gchar *path;
+	gchar *dirname;
+	gchar *basename;
+
+	GError *error = NULL;
+
+	gint i;
+
+
+	cmd = (gchar *) get_gconf_value (GCONF_SEND_TO_CMD_KEY);
+
+	if (! g_shell_parse_argv (cmd, & argc, & argv_parsed, NULL))
+		goto exit;
+
+	argv = g_new0 (gchar *, argc + 1);
+
+	path     = g_filename_from_uri (tile->uri, NULL, NULL);
+	dirname  = g_path_get_dirname  (path);
+	basename = g_path_get_basename (path);
+
+	for (i = 0; i < argc; ++i) {
+		if (strstr (argv_parsed [i], "DIRNAME"))
+			argv [i] = string_replace_once (argv_parsed [i], "DIRNAME", dirname);
+		else if (strstr (argv_parsed [i], "BASENAME"))
+			argv [i] = string_replace_once (argv_parsed [i], "BASENAME", basename);
+		else
+			argv [i] = g_strdup (argv_parsed [i]);
+	}
+
+	argv [argc] = NULL;
+
+	g_free (path);
+	g_free (dirname);
+	g_free (basename);
+
+	g_spawn_async (
+		NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
+		disown_spawned_child, NULL, NULL, & error);
+
+	if (error) {
+		cmd = g_strjoinv (" ", argv);
+		libslab_handle_g_error (
+			& error, "%s: can't execute search [%s]\n", G_STRFUNC, cmd);
+		g_free (cmd);
+	}
+
+	g_strfreev (argv);
+
+exit:
+
+	g_free (cmd);
+	g_strfreev (argv_parsed);
+}
+
+static void
+disown_spawned_child (gpointer user_data)
+{
+	setsid  ();
+	setpgrp ();
+}
+
+static void
+open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
+	GList *uris = NULL;
+	gboolean res;
+	GdkAppLaunchContext *launch_context;
+	GError *error = NULL;
+
+	if (priv->default_app)
+	{
+		uris = g_list_append (uris, TILE (tile)->uri);
+		
+		launch_context = gdk_app_launch_context_new ();
+		gdk_app_launch_context_set_screen (launch_context,
+						   gtk_widget_get_screen (GTK_WIDGET (tile)));
+		gdk_app_launch_context_set_timestamp (launch_context,
+						      event->time);
+
+		res = g_app_info_launch_uris (priv->default_app, uris,
+					      G_APP_LAUNCH_CONTEXT (launch_context),
+					      &error);
+
+		if (!res) {
+			g_warning
+				("error: could not launch application with [%s]: %s\n",
+				 TILE (tile)->uri, error->message);
+			g_error_free (error);
+		}
+
+		g_list_free (uris);
+		g_object_unref (launch_context);
+	} else {
+		gchar *cmd;
+		cmd = string_replace_once (
+			get_slab_gconf_string (SLAB_FILE_MANAGER_OPEN_CMD), "FILE_URI", tile->uri);
+		spawn_process (cmd);
+		g_free (cmd);
+	}
+}
diff --git a/libslab/directory-tile.h b/libslab/directory-tile.h
new file mode 100644
index 0000000..d6ef0d5
--- /dev/null
+++ b/libslab/directory-tile.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __DIRECTORY_TILE_H__
+#define __DIRECTORY_TILE_H__
+
+#include <time.h>
+
+#include "nameplate-tile.h"
+
+G_BEGIN_DECLS
+
+#define DIRECTORY_TILE_TYPE         (directory_tile_get_type ())
+#define DIRECTORY_TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DIRECTORY_TILE_TYPE, DirectoryTile))
+#define DIRECTORY_TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), DIRECTORY_TILE_TYPE, DirectoryTileClass))
+#define IS_DIRECTORY_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DIRECTORY_TILE_TYPE))
+#define IS_DIRECTORY_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), DIRECTORY_TILE_TYPE))
+#define DIRECTORY_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DIRECTORY_TILE_TYPE, DirectoryTileClass))
+
+typedef struct {
+	NameplateTile nameplate_tile;
+} DirectoryTile;
+
+typedef struct {
+	NameplateTileClass nameplate_tile_class;
+} DirectoryTileClass;
+
+#define DIRECTORY_TILE_ACTION_OPEN          0
+#define DIRECTORY_TILE_ACTION_RENAME        1
+#define DIRECTORY_TILE_ACTION_MOVE_TO_TRASH 2
+#define DIRECTORY_TILE_ACTION_DELETE        3
+#define DIRECTORY_TILE_ACTION_SEND_TO       4
+
+GType directory_tile_get_type (void);
+
+GtkWidget *directory_tile_new (const gchar *uri, const gchar *title, const gchar *icon_name, const gchar *mime_type);
+
+G_END_DECLS
+
+#endif
diff --git a/libslab/document-tile.c b/libslab/document-tile.c
new file mode 100644
index 0000000..33522a7
--- /dev/null
+++ b/libslab/document-tile.c
@@ -0,0 +1,1093 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006, 2007 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "document-tile.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include <libgnomeui/gnome-icon-lookup.h>
+#include <gio/gio.h>
+
+#include "slab-gnome-util.h"
+#include "gnome-utils.h"
+#include "libslab-utils.h"
+#include "bookmark-agent.h"
+
+#define GCONF_SEND_TO_CMD_KEY       "/desktop/gnome/applications/main-menu/file-area/file_send_to_cmd"
+#define GCONF_ENABLE_DELETE_KEY_DIR "/apps/nautilus/preferences"
+#define GCONF_ENABLE_DELETE_KEY     GCONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
+#define GCONF_CONFIRM_DELETE_KEY    GCONF_ENABLE_DELETE_KEY_DIR "/confirm_trash"
+
+G_DEFINE_TYPE (DocumentTile, document_tile, NAMEPLATE_TILE_TYPE)
+
+static void document_tile_finalize (GObject *);
+static void document_tile_style_set (GtkWidget *, GtkStyle *);
+
+static void document_tile_private_setup (DocumentTile *);
+static void load_image (DocumentTile *);
+
+static GtkWidget *create_header (const gchar *);
+
+static char      *create_subheader_string (time_t date);
+static GtkWidget *create_subheader (const gchar *);
+static void update_user_list_menu_item (DocumentTile *);
+
+static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer);
+
+static void open_with_default_trigger    (Tile *, TileEvent *, TileAction *);
+static void open_in_file_manager_trigger (Tile *, TileEvent *, TileAction *);
+static void rename_trigger               (Tile *, TileEvent *, TileAction *);
+static void move_to_trash_trigger        (Tile *, TileEvent *, TileAction *);
+static void delete_trigger               (Tile *, TileEvent *, TileAction *);
+static void user_docs_trigger            (Tile *, TileEvent *, TileAction *);
+static void send_to_trigger              (Tile *, TileEvent *, TileAction *);
+
+static void rename_entry_activate_cb (GtkEntry *, gpointer);
+static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer);
+
+static void gconf_enable_delete_cb (GConfClient *, guint, GConfEntry *, gpointer);
+
+static void agent_notify_cb (GObject *, GParamSpec *, gpointer);
+
+typedef struct
+{
+	gchar *basename;
+	gchar *mime_type;
+	time_t modified;
+
+	GAppInfo *default_app;
+
+	GtkBin *header_bin;
+
+	gboolean image_is_broken;
+	gchar * force_icon_name;  //show an icon instead of a thumbnail
+
+	gboolean delete_enabled;
+	guint gconf_conn_id;
+
+	BookmarkAgent       *agent;
+	BookmarkStoreStatus  store_status;
+	gboolean             is_bookmarked;
+	gulong               notify_signal_id;
+} DocumentTilePrivate;
+
+#define DOCUMENT_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DOCUMENT_TILE_TYPE, DocumentTilePrivate))
+
+static void document_tile_class_init (DocumentTileClass *this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
+
+	g_obj_class->finalize = document_tile_finalize;
+
+	widget_class->style_set = document_tile_style_set;
+
+	g_type_class_add_private (this_class, sizeof (DocumentTilePrivate));
+}
+
+//Use a specific icon instead of a thumbnail.
+GtkWidget *
+document_tile_new_force_icon (const gchar *in_uri, const gchar *mime_type, time_t modified, const gchar *icon)
+{
+	DocumentTile *this;
+	DocumentTilePrivate *priv;
+	
+	this = (DocumentTile *) document_tile_new (in_uri, mime_type, modified);
+	priv = DOCUMENT_TILE_GET_PRIVATE (this);
+	priv->force_icon_name = g_strdup (icon);
+	return GTK_WIDGET (this);
+}
+
+GtkWidget *
+document_tile_new (const gchar *in_uri, const gchar *mime_type, time_t modified)
+{
+	DocumentTile *this;
+	DocumentTilePrivate *priv;
+
+	gchar *uri;
+	GtkWidget *image;
+	GtkWidget *header;
+	GtkWidget *subheader;
+	GtkMenu *context_menu;
+
+	GtkContainer *menu_ctnr;
+	GtkWidget *menu_item;
+
+	TileAction *action;
+
+	gchar *basename;
+
+	gchar *time_str;
+
+	gchar *markup;
+
+	AtkObject *accessible;
+
+	GFile * file;
+	gchar *tooltip_text;
+
+	libslab_checkpoint ("document_tile_new(): start");
+  
+	uri = g_strdup (in_uri);
+
+	image = gtk_image_new ();
+
+	markup = g_path_get_basename (uri);
+	basename = g_uri_unescape_string (markup, NULL);
+	g_free (markup);
+
+	header = create_header (basename);
+
+	time_str = create_subheader_string (modified);
+	subheader = create_subheader (time_str);
+	
+	file = g_file_new_for_uri (uri);
+	tooltip_text = g_file_get_parse_name (file);
+	g_object_unref (file);
+	
+	context_menu = GTK_MENU (gtk_menu_new ());
+
+	this = g_object_new (DOCUMENT_TILE_TYPE, "tile-uri", uri, "nameplate-image", image,
+		"nameplate-header", header, "nameplate-subheader", subheader, 
+		"context-menu", context_menu, NULL);
+	gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text);
+
+	g_free (uri);
+	if (tooltip_text)
+		g_free (tooltip_text);
+
+	priv = DOCUMENT_TILE_GET_PRIVATE (this);
+	priv->basename    = g_strdup (basename);
+	priv->mime_type   = g_strdup (mime_type);
+	priv->modified    = modified;
+	priv->header_bin  = GTK_BIN (header);
+
+	document_tile_private_setup (this);
+
+	TILE (this)->actions = g_new0 (TileAction *, 7);
+	TILE (this)->n_actions = 7;
+
+	menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
+
+	/* make open with default action */
+
+	if (priv->default_app) {
+		markup = g_markup_printf_escaped (_("<b>Open with \"%s\"</b>"),
+						    g_app_info_get_name (priv->default_app));
+		action = tile_action_new (TILE (this), open_with_default_trigger, markup,
+			TILE_ACTION_OPENS_NEW_WINDOW);
+		g_free (markup);
+
+		TILE (this)->default_action = action;
+
+		menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action)));
+	}
+	else {
+		action = NULL;
+		menu_item = gtk_menu_item_new_with_label (_("Open with Default Application"));
+		gtk_widget_set_sensitive (menu_item, FALSE);
+	}
+
+	TILE (this)->actions[DOCUMENT_TILE_ACTION_OPEN_WITH_DEFAULT] = action;
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make open in nautilus action */
+
+	action = tile_action_new (TILE (this), open_in_file_manager_trigger,
+		_("Open in File Manager"), TILE_ACTION_OPENS_NEW_WINDOW);
+	TILE (this)->actions[DOCUMENT_TILE_ACTION_OPEN_IN_FILE_MANAGER] = action;
+
+	if (!TILE (this)->default_action)
+		TILE (this)->default_action = action;
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* insert separator */
+
+	menu_item = gtk_separator_menu_item_new ();
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make rename action */
+
+	action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0);
+	TILE (this)->actions[DOCUMENT_TILE_ACTION_RENAME] = action;
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make send to action */
+
+	/* Only allow Send To for local files, ideally this would use something
+	 * equivalent to gnome_vfs_uri_is_local, but that method will stat the file and
+	 * that can hang in some conditions. */
+
+	if (!strncmp (TILE (this)->uri, "file://", 7))
+	{
+		action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."),
+			TILE_ACTION_OPENS_NEW_WINDOW);
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	}
+	else
+	{
+		action = NULL;
+
+		menu_item = gtk_menu_item_new_with_label (_("Send To..."));
+		gtk_widget_set_sensitive (menu_item, FALSE);
+	}
+
+	TILE (this)->actions[DOCUMENT_TILE_ACTION_SEND_TO] = action;
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make "add/remove to favorites" action */
+
+	action = tile_action_new (TILE (this), user_docs_trigger, NULL, 0);
+	TILE (this)->actions [DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU] = action;
+
+	update_user_list_menu_item (this);
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* insert separator */
+
+	menu_item = gtk_separator_menu_item_new ();
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make move to trash action */
+
+	action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0);
+	TILE (this)->actions[DOCUMENT_TILE_ACTION_MOVE_TO_TRASH] = action;
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+	gtk_container_add (menu_ctnr, menu_item);
+
+	/* make delete action */
+
+	if (priv->delete_enabled)
+	{
+		action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0);
+		TILE (this)->actions[DOCUMENT_TILE_ACTION_DELETE] = action;
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_container_add (menu_ctnr, menu_item);
+	}
+
+	gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu));
+
+	accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
+	if (basename)
+	  atk_object_set_name (accessible, basename);
+	if (time_str)
+	  atk_object_set_description (accessible, time_str);
+
+	g_free (basename);
+	g_free (time_str);
+
+	libslab_checkpoint ("document_tile_new(): end");
+
+	return GTK_WIDGET (this);
+}
+
+static void
+document_tile_private_setup (DocumentTile *this)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this);
+
+	GFile *file;
+	GAppInfo *app;
+
+	GConfClient *client;
+	GError *error = NULL;
+
+	file = g_file_new_for_uri (TILE (this)->uri);
+	app = g_file_query_default_handler (file, NULL, &error);
+	priv->default_app = app;
+
+	if (error)
+		g_error_free (error);
+	g_object_unref (file);
+
+	priv->delete_enabled =
+		(gboolean) GPOINTER_TO_INT (get_gconf_value (GCONF_ENABLE_DELETE_KEY));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_add_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
+	priv->gconf_conn_id =
+		connect_gconf_notify (GCONF_ENABLE_DELETE_KEY, gconf_enable_delete_cb, this);
+
+	g_object_unref (client);
+
+	priv->agent = bookmark_agent_get_instance (BOOKMARK_STORE_USER_DOCS);
+
+	priv->notify_signal_id = g_signal_connect (
+		G_OBJECT (priv->agent), "notify", G_CALLBACK (agent_notify_cb), this);
+}
+
+static void
+document_tile_init (DocumentTile *tile)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	priv->basename         = NULL;
+	priv->mime_type        = NULL;
+	priv->modified         = 0;
+
+	priv->default_app      = NULL;
+
+	priv->header_bin       = NULL;
+
+	priv->image_is_broken  = TRUE;
+	priv->force_icon_name  = NULL;
+
+	priv->delete_enabled   = FALSE;
+	priv->gconf_conn_id    = 0;
+
+	priv->agent            = NULL;
+	priv->store_status     = BOOKMARK_STORE_DEFAULT;
+	priv->is_bookmarked    = FALSE;
+	priv->notify_signal_id = 0;
+}
+
+static void
+document_tile_finalize (GObject *g_object)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (g_object);
+
+	GConfClient *client;
+
+	g_free (priv->basename);
+	g_free (priv->mime_type);
+	g_free (priv->force_icon_name);
+
+	if (priv->default_app)
+		g_object_unref (priv->default_app);
+
+	if (priv->notify_signal_id)
+		g_signal_handler_disconnect (priv->agent, priv->notify_signal_id);
+
+	g_object_unref (G_OBJECT (priv->agent));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_notify_remove (client, priv->gconf_conn_id);
+	gconf_client_remove_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, NULL);
+
+	g_object_unref (client);
+
+	G_OBJECT_CLASS (document_tile_parent_class)->finalize (g_object);
+}
+
+static void
+document_tile_style_set (GtkWidget *widget, GtkStyle *prev_style)
+{
+	load_image (DOCUMENT_TILE (widget));
+}
+
+static void
+load_image (DocumentTile *tile)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	gchar *icon_id = NULL;
+	gboolean free_icon_id = TRUE;
+	GnomeThumbnailFactory *thumbnail_factory;
+
+	libslab_checkpoint ("document-tile.c: load_image(): start for %s", TILE (tile)->uri);
+
+	if (priv->force_icon_name || ! priv->mime_type) {
+		if (priv->force_icon_name)
+			icon_id = priv->force_icon_name;
+		else
+			icon_id = "gnome-fs-regular";
+		free_icon_id = FALSE;
+
+		goto exit;
+	}
+
+	thumbnail_factory = libslab_thumbnail_factory_get ();
+
+	icon_id = gnome_thumbnail_factory_lookup (thumbnail_factory, TILE (tile)->uri, priv->modified);
+
+	if (! icon_id)
+		icon_id = gnome_icon_lookup (
+			gtk_icon_theme_get_default (), thumbnail_factory,
+			TILE (tile)->uri, NULL, NULL, priv->mime_type, 0, NULL);
+
+exit:
+
+	priv->image_is_broken = slab_load_image (
+		GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_id);
+
+	if (free_icon_id && icon_id)
+		g_free (icon_id);
+
+	libslab_checkpoint ("document-tile.c: load_image(): end");
+}
+
+/* Next function taken from e-data-server-util.c in evolution-data-server */
+/** 
+ * e_strftime:
+ * @s: The string array to store the result in.
+ * @max: The size of array @s.
+ * @fmt: The formatting to use on @tm.
+ * @tm: The time value to format.
+ *
+ * This function is a wrapper around the strftime(3) function, which
+ * converts the &percnt;l and &percnt;k (12h and 24h) format variables if necessary.
+ *
+ * Returns: The number of characters placed in @s.
+ **/
+static size_t
+e_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+#ifdef HAVE_LKSTRFTIME
+	return strftime(s, max, fmt, tm);
+#else
+	char *c, *ffmt, *ff;
+	size_t ret;
+
+	ffmt = g_strdup(fmt);
+	ff = ffmt;
+	while ((c = strstr(ff, "%l")) != NULL) {
+		c[1] = 'I';
+		ff = c;
+	}
+
+	ff = ffmt;
+	while ((c = strstr(ff, "%k")) != NULL) {
+		c[1] = 'H';
+		ff = c;
+	}
+
+#ifdef G_OS_WIN32
+	/* The Microsoft strftime() doesn't have %e either */
+	ff = ffmt;
+	while ((c = strstr(ff, "%e")) != NULL) {
+		c[1] = 'd';
+		ff = c;
+	}
+#endif
+
+	ret = strftime(s, max, ffmt, tm);
+	g_free(ffmt);
+	return ret;
+#endif
+}
+
+/* Next two functions taken from e-util.c in evolution */
+/**
+ * Function to do a last minute fixup of the AM/PM stuff if the locale
+ * and gettext haven't done it right. Most English speaking countries
+ * except the USA use the 24 hour clock (UK, Australia etc). However
+ * since they are English nobody bothers to write a language
+ * translation (gettext) file. So the locale turns off the AM/PM, but
+ * gettext does not turn on the 24 hour clock. Leaving a mess.
+ *
+ * This routine checks if AM/PM are defined in the locale, if not it
+ * forces the use of the 24 hour clock.
+ *
+ * The function itself is a front end on strftime and takes exactly
+ * the same arguments.
+ *
+ * TODO: Actually remove the '%p' from the fixed up string so that
+ * there isn't a stray space.
+ **/
+
+static size_t
+e_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+	char buf[10];
+	char *sp;
+	char *ffmt;
+	size_t ret;
+
+	if (strstr(fmt, "%p")==NULL && strstr(fmt, "%P")==NULL) {
+		/* No AM/PM involved - can use the fmt string directly */
+		ret=e_strftime(s, max, fmt, tm);
+	} else {
+		/* Get the AM/PM symbol from the locale */
+		e_strftime (buf, 10, "%p", tm);
+
+		if (buf[0]) {
+			/**
+			 * AM/PM have been defined in the locale
+			 * so we can use the fmt string directly
+			 **/
+			ret=e_strftime(s, max, fmt, tm);
+		} else {
+			/**
+			 * No AM/PM defined by locale
+			 * must change to 24 hour clock
+			 **/
+			ffmt=g_strdup(fmt);
+			for (sp=ffmt; (sp=strstr(sp, "%l")); sp++) {
+				/**
+				 * Maybe this should be 'k', but I have never
+				 * seen a 24 clock actually use that format
+				 **/
+				sp[1]='H';
+			}
+			for (sp=ffmt; (sp=strstr(sp, "%I")); sp++) {
+				sp[1]='H';
+			}
+			ret=e_strftime(s, max, ffmt, tm);
+			g_free(ffmt);
+		}
+	}
+
+	return(ret);
+}
+
+static size_t 
+e_utf8_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+	size_t sz, ret;
+	char *locale_fmt, *buf;
+
+	locale_fmt = g_locale_from_utf8(fmt, -1, NULL, &sz, NULL);
+	if (!locale_fmt)
+		return 0;
+
+	ret = e_strftime_fix_am_pm(s, max, locale_fmt, tm);
+	if (!ret) {
+		g_free (locale_fmt);
+		return 0;
+	}
+
+	buf = g_locale_to_utf8(s, ret, NULL, &sz, NULL);
+	if (!buf) {
+		g_free (locale_fmt);
+		return 0;
+	}
+
+	if (sz >= max) {
+		char *tmp = buf + max - 1;
+		tmp = g_utf8_find_prev_char(buf, tmp);
+		if (tmp)
+			sz = tmp - buf;
+		else
+			sz = 0;
+	}
+	memcpy(s, buf, sz);
+	s[sz] = '\0';
+	g_free(locale_fmt);
+	g_free(buf);
+	return sz;
+}
+
+static char *
+create_subheader_string (time_t date)
+{
+	time_t nowdate = time(NULL);
+	time_t yesdate;
+	struct tm then, now, yesterday;
+	char buf[100];
+	gboolean done = FALSE;
+
+	if (date == 0) {
+		return g_strdup (_("?"));
+	}
+
+	localtime_r (&date, &then);
+	localtime_r (&nowdate, &now);
+
+	if (nowdate - date < 60 * 60 * 8 && nowdate > date) {
+		e_utf8_strftime_fix_am_pm (buf, 100, _("%l:%M %p"), &then);
+		done = TRUE;
+	}
+
+	if (!done) {
+		if (then.tm_mday == now.tm_mday &&
+		    then.tm_mon == now.tm_mon &&
+		    then.tm_year == now.tm_year) {
+			e_utf8_strftime_fix_am_pm (buf, 100, _("Today %l:%M %p"), &then);
+			done = TRUE;
+		}
+	}
+	if (!done) {
+		yesdate = nowdate - 60 * 60 * 24;
+		localtime_r (&yesdate, &yesterday);
+		if (then.tm_mday == yesterday.tm_mday &&
+		    then.tm_mon == yesterday.tm_mon &&
+		    then.tm_year == yesterday.tm_year) {
+			e_utf8_strftime_fix_am_pm (buf, 100, _("Yesterday %l:%M %p"), &then);
+			done = TRUE;
+		}
+	}
+	if (!done) {
+		int i;
+		for (i = 2; i < 7; i++) {
+			yesdate = nowdate - 60 * 60 * 24 * i;
+			localtime_r (&yesdate, &yesterday);
+			if (then.tm_mday == yesterday.tm_mday &&
+			    then.tm_mon == yesterday.tm_mon &&
+			    then.tm_year == yesterday.tm_year) {
+				e_utf8_strftime_fix_am_pm (buf, 100, _("%a %l:%M %p"), &then);
+				done = TRUE;
+				break;
+			}
+		}
+	}
+	if (!done) {
+		if (then.tm_year == now.tm_year) {
+			e_utf8_strftime_fix_am_pm (buf, 100, _("%b %d %l:%M %p"), &then);
+		} else {
+			e_utf8_strftime_fix_am_pm (buf, 100, _("%b %d %Y"), &then);
+		}
+	}
+
+	return g_strdup (g_strstrip (buf));
+}
+
+static GtkWidget *
+create_header (const gchar *name)
+{
+	GtkWidget *header_bin;
+	GtkWidget *header;
+
+	header = gtk_label_new (name);
+	gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+	gtk_container_add (GTK_CONTAINER (header_bin), header);
+
+	g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb),
+		NULL);
+
+	return header_bin;
+}
+
+static GtkWidget *
+create_subheader (const gchar *desc)
+{
+	GtkWidget *subheader;
+
+	subheader = gtk_label_new (desc);
+	gtk_label_set_ellipsize (GTK_LABEL (subheader), PANGO_ELLIPSIZE_END);
+	gtk_misc_set_alignment (GTK_MISC (subheader), 0.0, 0.5);
+	gtk_widget_modify_fg (subheader, GTK_STATE_NORMAL,
+		&subheader->style->fg[GTK_STATE_INSENSITIVE]);
+
+	return subheader;
+}
+
+static void
+update_user_list_menu_item (DocumentTile *this)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this);
+
+	TileAction  *action;
+	GtkMenuItem *item;
+
+
+	action = TILE (this)->actions [DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU];
+
+	if (! action)
+		return;
+
+	priv->is_bookmarked = bookmark_agent_has_item (priv->agent, TILE (this)->uri);
+
+	if (priv->is_bookmarked)
+		tile_action_set_menu_item_label (action, _("Remove from Favorites"));
+	else
+		tile_action_set_menu_item_label (action, _("Add to Favorites"));
+
+	item = tile_action_get_menu_item (action);
+
+	if (! GTK_IS_MENU_ITEM (item))
+		return;
+
+	g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->store_status, NULL);
+
+	gtk_widget_set_sensitive (GTK_WIDGET (item), (priv->store_status != BOOKMARK_STORE_DEFAULT_ONLY));
+}
+
+static void
+header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
+{
+	gtk_widget_set_size_request (widget, alloc->width, -1);
+}
+
+static void
+rename_entry_activate_cb (GtkEntry *entry, gpointer user_data)
+{
+	DocumentTile *tile = DOCUMENT_TILE (user_data);
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	GFile *src_file;
+	GFile *dst_file;
+
+	char *src_path;
+	char *dirname;
+	char *dst_path;
+
+	gboolean res;
+	GError *error = NULL;
+
+	GtkWidget *child;
+	GtkWidget *header;
+
+	if (strlen (gtk_entry_get_text (entry)) < 1)
+		return;
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL);
+	dirname = g_path_get_dirname (src_path);
+	dst_path = g_build_filename (dirname, gtk_entry_get_text (entry), NULL);
+	dst_file = g_file_new_for_path (dst_path);
+
+	res = g_file_move (src_file, dst_file, 0, NULL, NULL, NULL, &error);
+	
+	if (res) {
+		char *dst_uri;
+
+		dst_uri = g_file_get_uri (dst_file);
+		bookmark_agent_move_item (priv->agent, TILE (tile)->uri, dst_uri);
+		g_free (dst_uri);
+
+		g_free (priv->basename);
+		priv->basename = g_strdup (gtk_entry_get_text (entry));
+	}
+	else {
+		g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri,
+			   dst_path, error->message);
+		g_error_free (error);
+	}
+
+	header = gtk_label_new (priv->basename);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	child = gtk_bin_get_child (priv->header_bin);
+
+	if (child)
+		gtk_widget_destroy (child);
+
+	gtk_container_add (GTK_CONTAINER (priv->header_bin), header);
+
+	gtk_widget_show (header);
+
+	g_object_unref (src_file);
+	g_object_unref (dst_file);
+
+	g_free (dirname);
+	g_free (dst_path);
+	g_free (src_path);
+}
+
+static gboolean
+rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void
+gconf_enable_delete_cb (GConfClient *client, guint conn_id, GConfEntry *entry, gpointer user_data)
+{
+	Tile *tile = TILE (user_data);
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (user_data);
+
+	GtkMenuShell *menu;
+	gboolean delete_enabled;
+
+	TileAction *action;
+	GtkWidget *menu_item;
+
+	menu = GTK_MENU_SHELL (tile->context_menu);
+
+	delete_enabled = gconf_value_get_bool (entry->value);
+
+	if (delete_enabled == priv->delete_enabled)
+		return;
+
+	priv->delete_enabled = delete_enabled;
+
+	if (priv->delete_enabled)
+	{
+		action = tile_action_new (tile, delete_trigger, _("Delete"), 0);
+		tile->actions[DOCUMENT_TILE_ACTION_DELETE] = action;
+
+		menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+		gtk_menu_shell_insert (menu, menu_item, 7);
+
+		gtk_widget_show_all (menu_item);
+	}
+	else
+	{
+		g_object_unref (tile->actions[DOCUMENT_TILE_ACTION_DELETE]);
+
+		tile->actions[DOCUMENT_TILE_ACTION_DELETE] = NULL;
+	}
+}
+
+static void
+open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	GList *uris = NULL;
+	gboolean res;
+	GdkAppLaunchContext *launch_context;
+	GError *error = NULL;
+
+	if (priv->default_app)
+	{
+		uris = g_list_append (uris, TILE (tile)->uri);
+		launch_context = gdk_app_launch_context_new ();
+		gdk_app_launch_context_set_screen (launch_context,
+						   gtk_widget_get_screen (GTK_WIDGET (tile)));
+		gdk_app_launch_context_set_timestamp (launch_context,
+						      event->time);
+
+		res = g_app_info_launch_uris (priv->default_app, uris,
+					      G_APP_LAUNCH_CONTEXT (launch_context), &error);
+
+		if (!res) {
+			g_warning
+				("error: could not launch application with [%s]: %s\n",
+				TILE (tile)->uri, error->message);
+			g_error_free (error);
+		}
+
+		g_list_free (uris);
+		g_object_unref (launch_context);
+	}
+}
+
+static void
+open_in_file_manager_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	GFile *filename;
+	GFile *dirname;
+	gchar *uri;
+
+	gchar *cmd;
+
+	filename = g_file_new_for_uri (TILE (tile)->uri);
+	dirname = g_file_get_parent (filename);
+	uri = g_file_get_uri (dirname);
+	
+	if (!uri)
+		g_warning ("error getting dirname for [%s]\n", TILE (tile)->uri);
+	else
+	{
+		cmd = string_replace_once (get_slab_gconf_string (SLAB_FILE_MANAGER_OPEN_CMD),
+			"FILE_URI", uri);
+		spawn_process (cmd);
+
+		g_free (cmd);
+	}
+
+	g_object_unref (filename);
+	g_object_unref (dirname);
+	g_free (uri);
+}
+
+static void
+rename_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	GtkWidget *child;
+	GtkWidget *entry;
+
+
+	entry = gtk_entry_new ();
+	gtk_entry_set_text (GTK_ENTRY (entry), priv->basename);
+	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+
+	child = gtk_bin_get_child (priv->header_bin);
+
+	if (child)
+		gtk_widget_destroy (child);
+
+	gtk_container_add (GTK_CONTAINER (priv->header_bin), entry);
+
+	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile);
+
+	g_signal_connect (G_OBJECT (entry), "key_release_event",
+		G_CALLBACK (rename_entry_key_release_cb), NULL);
+
+	gtk_widget_show (entry);
+	gtk_widget_grab_focus (entry);
+}
+
+static void
+move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	GFile *src_file;
+	gboolean res;
+	GError *error = NULL;
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	res = g_file_trash (src_file, NULL, &error);
+
+	if (res)
+		bookmark_agent_remove_item (priv->agent, TILE (tile)->uri);
+	else {
+		g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri,
+			   error->message);
+
+		g_error_free (error);
+	}
+
+	g_object_unref (src_file);
+}
+
+static void
+delete_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+
+	GtkDialog *confirm_dialog;
+	gint       result;
+
+	GFile *src_file;
+	gboolean res;
+	GError *error = NULL;
+
+
+	if (GPOINTER_TO_INT (libslab_get_gconf_value (GCONF_CONFIRM_DELETE_KEY))) {
+		confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, 
+				GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), priv->basename));
+		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost."));
+							
+		gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+		gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES);
+		gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES);
+
+		result = gtk_dialog_run (confirm_dialog);
+
+		gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
+
+		if (result != GTK_RESPONSE_YES)
+			return;
+	}
+
+	src_file = g_file_new_for_uri (TILE (tile)->uri);
+
+	res = g_file_delete (src_file, NULL, &error);
+
+	if (res)
+		bookmark_agent_remove_item (priv->agent, TILE (tile)->uri);
+	else {
+		g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri,
+			   error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (src_file);
+}
+
+static void
+user_docs_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	DocumentTile *this        = DOCUMENT_TILE             (tile);
+	DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this);
+
+	BookmarkItem *item;
+
+
+	if (priv->is_bookmarked)
+		bookmark_agent_remove_item (priv->agent, tile->uri);
+	else {
+		item = g_new0 (BookmarkItem, 1);
+		item->uri       = tile->uri;
+		item->mime_type = priv->mime_type;
+		item->mtime     = priv->modified;
+		if (priv->default_app) {
+			item->app_name  = (gchar *) g_app_info_get_name (priv->default_app);
+			item->app_exec  = (gchar *) g_app_info_get_executable (priv->default_app);
+		}
+
+		bookmark_agent_add_item (priv->agent, item);
+		g_free (item);
+	}
+
+	update_user_list_menu_item (this);
+}
+
+static void
+send_to_trigger (Tile *tile, TileEvent *event, TileAction *action)
+{
+	gchar *cmd;
+	gchar **argv;
+
+	gchar *filename;
+	gchar *dirname;
+	gchar *basename;
+
+	GError *error = NULL;
+
+	gchar *tmp;
+	gint i;
+
+	cmd = (gchar *) get_gconf_value (GCONF_SEND_TO_CMD_KEY);
+	argv = g_strsplit (cmd, " ", 0);
+
+	filename = g_filename_from_uri (TILE (tile)->uri, NULL, NULL);
+	dirname = g_path_get_dirname (filename);
+	basename = g_path_get_basename (filename);
+
+	for (i = 0; argv[i]; ++i)
+	{
+		if (strstr (argv[i], "DIRNAME"))
+		{
+			tmp = string_replace_once (argv[i], "DIRNAME", dirname);
+			g_free (argv[i]);
+			argv[i] = tmp;
+		}
+
+		if (strstr (argv[i], "BASENAME"))
+		{
+			tmp = string_replace_once (argv[i], "BASENAME", basename);
+			g_free (argv[i]);
+			argv[i] = tmp;
+		}
+	}
+
+	gdk_spawn_on_screen (gtk_widget_get_screen (GTK_WIDGET (tile)), NULL, argv, NULL,
+		G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
+
+	if (error)
+		handle_g_error (&error, "error in %s", G_STRFUNC);
+
+	g_free (cmd);
+	g_free (filename);
+	g_free (dirname);
+	g_free (basename);
+	g_strfreev (argv);
+}
+
+static void
+agent_notify_cb (GObject *g_obj, GParamSpec *pspec, gpointer user_data)
+{
+	update_user_list_menu_item (DOCUMENT_TILE (user_data));
+}
diff --git a/libslab/document-tile.h b/libslab/document-tile.h
new file mode 100644
index 0000000..0e2e83d
--- /dev/null
+++ b/libslab/document-tile.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __DOCUMENT_TILE_H__
+#define __DOCUMENT_TILE_H__
+
+#include <time.h>
+
+#include "nameplate-tile.h"
+
+G_BEGIN_DECLS
+
+#define DOCUMENT_TILE_TYPE         (document_tile_get_type ())
+#define DOCUMENT_TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DOCUMENT_TILE_TYPE, DocumentTile))
+#define DOCUMENT_TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), DOCUMENT_TILE_TYPE, DocumentTileClass))
+#define IS_DOCUMENT_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DOCUMENT_TILE_TYPE))
+#define IS_DOCUMENT_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), DOCUMENT_TILE_TYPE))
+#define DOCUMENT_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DOCUMENT_TILE_TYPE, DocumentTileClass))
+
+typedef struct {
+	NameplateTile nameplate_tile;
+} DocumentTile;
+
+typedef struct {
+	NameplateTileClass nameplate_tile_class;
+} DocumentTileClass;
+
+#define DOCUMENT_TILE_ACTION_OPEN_WITH_DEFAULT    0
+#define DOCUMENT_TILE_ACTION_OPEN_IN_FILE_MANAGER 1
+#define DOCUMENT_TILE_ACTION_RENAME               2
+#define DOCUMENT_TILE_ACTION_MOVE_TO_TRASH        3
+#define DOCUMENT_TILE_ACTION_DELETE               4
+#define DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU     5
+#define DOCUMENT_TILE_ACTION_SEND_TO              6
+#define DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS       7 /* must be last entry and equal to the number of actions */
+
+GType document_tile_get_type (void);
+
+GtkWidget *document_tile_new (const gchar *uri, const gchar *mime_type, time_t modified);
+
+//If you want to show a icon instead of a thumbnail
+GtkWidget *document_tile_new_force_icon (const gchar *uri, const gchar *mime_type, time_t modified, const gchar *icon);
+
+G_END_DECLS
+
+#endif
diff --git a/libslab/double-click-detector.c b/libslab/double-click-detector.c
new file mode 100644
index 0000000..8ddc9cf
--- /dev/null
+++ b/libslab/double-click-detector.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006, 2007 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "double-click-detector.h"
+
+#include <gtk/gtksettings.h>
+
+#include "libslab-utils.h"
+
+static void double_click_detector_class_init (DoubleClickDetectorClass *);
+static void double_click_detector_init (DoubleClickDetector *);
+static void double_click_detector_dispose (GObject *);
+
+GType
+double_click_detector_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info = {
+			sizeof (DoubleClickDetectorClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) double_click_detector_class_init,
+			NULL,
+			NULL,
+			sizeof (DoubleClickDetector),
+			0,
+			(GInstanceInitFunc) double_click_detector_init
+		};
+
+		object_type =
+			g_type_register_static (G_TYPE_OBJECT, "DoubleClickDetector", &object_info,
+			0);
+	}
+
+	return object_type;
+}
+
+static void
+double_click_detector_class_init (DoubleClickDetectorClass * detector_class)
+{
+	GObjectClass *g_obj_class = (GObjectClass *) detector_class;
+
+	g_obj_class->dispose = double_click_detector_dispose;
+}
+
+static void
+double_click_detector_init (DoubleClickDetector * detector)
+{
+	GtkSettings *settings;
+	gint click_interval;
+
+	settings = gtk_settings_get_default ();
+
+	g_object_get (G_OBJECT (settings), "gtk-double-click-time", &click_interval, NULL);
+
+	detector->double_click_time = (guint32) click_interval;
+	detector->last_click_time = 0;
+}
+
+DoubleClickDetector *
+double_click_detector_new ()
+{
+	return g_object_new (DOUBLE_CLICK_DETECTOR_TYPE, NULL);
+}
+
+static void
+double_click_detector_dispose (GObject * obj)
+{
+}
+
+gboolean
+double_click_detector_is_double_click (DoubleClickDetector *this, guint32 event_time,
+                                       gboolean auto_update)
+{
+	gint32 delta;
+
+	if (event_time <= 0)
+		event_time = libslab_get_current_time_millis ();
+
+	if (this->last_click_time <= 0) {
+		if (auto_update)
+			double_click_detector_update_click_time (this, event_time);
+
+		return FALSE;
+	}
+
+	delta = event_time - this->last_click_time;
+
+	if (auto_update)
+		double_click_detector_update_click_time (this, event_time);
+
+	return delta < this->double_click_time;
+}
+
+void
+double_click_detector_update_click_time (DoubleClickDetector *this, guint32 event_time)
+{
+	if (event_time <= 0)
+		event_time = libslab_get_current_time_millis ();
+
+	this->last_click_time = event_time;
+}
diff --git a/libslab/double-click-detector.h b/libslab/double-click-detector.h
new file mode 100644
index 0000000..fed2938
--- /dev/null
+++ b/libslab/double-click-detector.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __DOUBLE_CLICK_DETECTOR_H__
+#define __DOUBLE_CLICK_DETECTOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DOUBLE_CLICK_DETECTOR_TYPE         (double_click_detector_get_type ())
+#define DOUBLE_CLICK_DETECTOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DOUBLE_CLICK_DETECTOR_TYPE, DoubleClickDetector))
+#define DOUBLE_CLICK_DETECTOR_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), DOUBLE_CLICK_DETECTOR_TYPE, DoubleClickDetectorClass))
+#define IS_DOUBLE_CLICK_DETECTOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DOUBLE_CLICK_DETECTOR_TYPE))
+#define IS_DOUBLE_CLICK_DETECTOR_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), DOUBLE_CLICK_DETECTOR_TYPE))
+#define DOUBLE_CLICK_DETECTOR_GET_CLASS(o) (G_TYPE_CHECK_GET_CLASS ((o), DOUBLE_CLICK_DETECTOR_TYPE, DoubleClickDetectorClass))
+
+typedef struct
+{
+	GObject parent_placeholder;
+
+	guint32 double_click_time;
+	guint32 last_click_time;
+} DoubleClickDetector;
+
+typedef struct
+{
+	GObjectClass parent_class;
+} DoubleClickDetectorClass;
+
+GType double_click_detector_get_type (void);
+
+DoubleClickDetector *double_click_detector_new (void);
+
+gboolean double_click_detector_is_double_click (DoubleClickDetector * detector, guint32 event_time,
+	gboolean auto_update);
+
+void double_click_detector_update_click_time (DoubleClickDetector * detector, guint32 event_time);
+
+G_END_DECLS
+#endif /* __DOUBLE_CLICK_DETECTOR_H__ */
diff --git a/libslab/gnome-utils.c b/libslab/gnome-utils.c
new file mode 100644
index 0000000..ad5c767
--- /dev/null
+++ b/libslab/gnome-utils.c
@@ -0,0 +1,346 @@
+#include "gnome-utils.h"
+
+#include <string.h>
+
+static void section_header_style_set (GtkWidget *, GtkStyle *, gpointer);
+
+gboolean
+load_image_by_id (GtkImage * image, GtkIconSize size, const gchar * image_id)
+{
+	GdkPixbuf *pixbuf;
+	gint width;
+	gint height;
+
+	GtkIconTheme *icon_theme;
+
+	gchar *id;
+
+	gboolean icon_exists;
+
+	if (!image_id)
+		return FALSE;
+
+	id = g_strdup (image_id);
+
+	gtk_icon_size_lookup (size, &width, &height);
+
+	if (g_path_is_absolute (id))
+	{
+		pixbuf = gdk_pixbuf_new_from_file_at_size (id, width, height, NULL);
+
+		icon_exists = (pixbuf != NULL);
+
+		if (icon_exists)
+		{
+			gtk_image_set_from_pixbuf (image, pixbuf);
+
+			g_object_unref (pixbuf);
+		}
+		else
+			gtk_image_set_from_stock (image, "gtk-missing-image", size);
+	}
+	else
+	{
+		if (		/* file extensions are not copesetic with loading by "name" */
+			g_str_has_suffix (id, ".png") ||
+			g_str_has_suffix (id, ".svg") ||
+			g_str_has_suffix (id, ".xpm")
+		   )
+
+			id[strlen (id) - 4] = '\0';
+
+		if (gtk_widget_has_screen (GTK_WIDGET (image)))
+			icon_theme =
+				gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET
+					(image)));
+		else
+			icon_theme = gtk_icon_theme_get_default ();
+
+		icon_exists = gtk_icon_theme_has_icon (icon_theme, id);
+
+		if (icon_exists)
+			gtk_image_set_from_icon_name (image, id, size);
+		else
+			gtk_image_set_from_stock (image, "gtk-missing-image", size);
+
+	}
+
+	g_free (id);
+
+	return icon_exists;
+}
+
+GnomeDesktopItem *
+load_desktop_item_by_unknown_id (const gchar * id)
+{
+	GnomeDesktopItem *item;
+	GError *error = NULL;
+
+	item = gnome_desktop_item_new_from_uri (id, 0, &error);
+
+	if (!error)
+		return item;
+	else
+	{
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_file (id, 0, &error);
+
+	if (!error)
+		return item;
+	else
+	{
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_basename (id, 0, &error);
+
+	if (!error)
+		return item;
+	else
+	{
+		g_error_free (error);
+		error = NULL;
+	}
+
+	return NULL;
+}
+
+gpointer
+get_gconf_value (const gchar * key)
+{
+	GConfClient *client;
+	GConfValue *value;
+	GError *error = NULL;
+
+	gpointer retval = NULL;
+
+	GList *list;
+	GSList *slist;
+
+	GConfValue *value_i;
+	GSList *node;
+
+	client = gconf_client_get_default ();
+	value = gconf_client_get (client, key, &error);
+
+	if (error || ! value)
+	{
+		handle_g_error (&error, "%s: error getting %s", G_STRFUNC, key);
+
+		goto exit;
+	}
+
+	switch (value->type)
+	{
+	case GCONF_VALUE_STRING:
+		retval = (gpointer) g_strdup (gconf_value_get_string (value));
+		break;
+
+	case GCONF_VALUE_INT:
+		retval = GINT_TO_POINTER (gconf_value_get_int (value));
+		break;
+
+	case GCONF_VALUE_BOOL:
+		retval = GINT_TO_POINTER (gconf_value_get_bool (value));
+		break;
+
+	case GCONF_VALUE_LIST:
+		list = NULL;
+		slist = gconf_value_get_list (value);
+
+		for (node = slist; node; node = node->next)
+		{
+			value_i = (GConfValue *) node->data;
+
+			if (value_i->type == GCONF_VALUE_STRING)
+				list = g_list_append (list,
+					g_strdup (gconf_value_get_string (value_i)));
+			else if (value_i->type == GCONF_VALUE_INT)
+				list = g_list_append (list,
+					GINT_TO_POINTER (gconf_value_get_int (value_i)));
+			else
+				g_assert_not_reached ();
+		}
+
+		retval = (gpointer) list;
+
+		break;
+
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	exit:
+
+	g_object_unref (client);
+	if(value)
+		gconf_value_free (value);
+
+	return retval;
+}
+
+void
+set_gconf_value (const gchar * key, gconstpointer data)
+{
+	GConfClient *client;
+	GConfValue *value;
+
+	GConfValueType type;
+	GConfValueType list_type;
+
+	GSList *slist = NULL;
+
+	GError *error = NULL;
+
+	GConfValue *value_i;
+	GList *node;
+
+	client = gconf_client_get_default ();
+	value = gconf_client_get (client, key, &error);
+
+	if (error)
+	{
+		handle_g_error (&error, "%s: error getting %s", G_STRFUNC, key);
+
+		goto exit;
+	}
+
+	type = value->type;
+	list_type =
+		(type ==
+		GCONF_VALUE_LIST ? gconf_value_get_list_type (value) : GCONF_VALUE_INVALID);
+
+	gconf_value_free (value);
+	value = gconf_value_new (type);
+
+	if (type == GCONF_VALUE_LIST)
+		gconf_value_set_list_type (value, list_type);
+
+	switch (type)
+	{
+	case GCONF_VALUE_STRING:
+		gconf_value_set_string (value, g_strdup ((gchar *) data));
+		break;
+
+	case GCONF_VALUE_INT:
+		gconf_value_set_int (value, GPOINTER_TO_INT (data));
+		break;
+
+	case GCONF_VALUE_BOOL:
+		gconf_value_set_bool (value, GPOINTER_TO_INT (data));
+		break;
+
+	case GCONF_VALUE_LIST:
+		for (node = (GList *) data; node; node = node->next)
+		{
+			value_i = gconf_value_new (list_type);
+
+			if (list_type == GCONF_VALUE_STRING)
+				gconf_value_set_string (value_i, (const gchar *) node->data);
+			else if (list_type == GCONF_VALUE_INT)
+				gconf_value_set_int (value_i, GPOINTER_TO_INT (node->data));
+			else
+				g_assert_not_reached ();
+
+			slist = g_slist_append (slist, value_i);
+		}
+
+		gconf_value_set_list_nocopy (value, slist);
+
+		break;
+
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	gconf_client_set (client, key, value, &error);
+
+	if (error)
+		handle_g_error (&error, "%s: error setting %s", G_STRFUNC, key);
+
+      exit:
+
+	gconf_value_free (value);
+	g_object_unref (client);
+}
+
+guint
+connect_gconf_notify (const gchar * key, GConfClientNotifyFunc cb, gpointer user_data)
+{
+	GConfClient *client;
+	guint conn_id;
+
+	GError *error = NULL;
+
+	client = gconf_client_get_default ();
+	conn_id = gconf_client_notify_add (client, key, cb, user_data, NULL, &error);
+
+	if (error)
+		handle_g_error (&error, "%s: error adding notify for (%s)", G_STRFUNC, key);
+
+	g_object_unref (client);
+
+	return conn_id;
+}
+
+void
+handle_g_error (GError ** error, const gchar * msg_format, ...)
+{
+	gchar *msg;
+	va_list args;
+
+	va_start (args, msg_format);
+	msg = g_strdup_vprintf (msg_format, args);
+	va_end (args);
+
+	if (*error)
+	{
+		g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+			"\nGError raised: [%s]\nuser_message: [%s]\n", (*error)->message, msg);
+
+		g_error_free (*error);
+
+		*error = NULL;
+	}
+	else
+		g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "\nerror raised: [%s]\n", msg);
+
+	g_free (msg);
+}
+
+GtkWidget *
+get_main_menu_section_header (const gchar * markup)
+{
+	GtkWidget *label;
+	gchar *text;
+
+	text = g_strdup_printf ("<span size=\"large\">%s</span>", markup);
+
+	label = gtk_label_new (text);
+	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+	gtk_widget_set_name (label, "gnome-main-menu-section-header");
+
+	g_signal_connect (G_OBJECT (label), "style-set", G_CALLBACK (section_header_style_set),
+		NULL);
+
+	g_free (text);
+
+	return label;
+}
+
+static void
+section_header_style_set (GtkWidget * widget, GtkStyle * prev_style, gpointer user_data)
+{
+	if (prev_style
+		&& widget->style->fg[GTK_STATE_SELECTED].green ==
+		prev_style->fg[GTK_STATE_SELECTED].green)
+		return;
+
+	gtk_widget_modify_fg (widget, GTK_STATE_NORMAL, &widget->style->bg[GTK_STATE_SELECTED]);
+}
diff --git a/libslab/gnome-utils.h b/libslab/gnome-utils.h
new file mode 100644
index 0000000..a6b7723
--- /dev/null
+++ b/libslab/gnome-utils.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GNOME_UTILS_H__
+#define __GNOME_UTILS_H__
+
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+#include <libgnome/gnome-desktop-item.h>
+
+G_BEGIN_DECLS
+
+gboolean load_image_by_id (GtkImage * image, GtkIconSize size,
+	const gchar * image_id);
+GnomeDesktopItem *load_desktop_item_by_unknown_id (const gchar * id);
+gpointer get_gconf_value (const gchar * key);
+void set_gconf_value (const gchar * key, gconstpointer data);
+guint connect_gconf_notify (const gchar * key, GConfClientNotifyFunc cb, gpointer user_data);
+void handle_g_error (GError ** error, const gchar * user_format, ...);
+GtkWidget *get_main_menu_section_header (const gchar * markup);
+
+G_END_DECLS
+#endif /* __GNOME_UTILS_H__ */
diff --git a/libslab/libslab-utils.c b/libslab/libslab-utils.c
new file mode 100644
index 0000000..587d5a3
--- /dev/null
+++ b/libslab/libslab-utils.c
@@ -0,0 +1,695 @@
+#include "libslab-utils.h"
+
+#ifdef HAVE_CONFIG_H
+#	include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <gconf/gconf-value.h>
+#include <libgnome/gnome-url.h>
+
+#define DESKTOP_ITEM_TERMINAL_EMULATOR_FLAG "TerminalEmulator"
+#define ALTERNATE_DOCPATH_KEY               "DocPath"
+
+static FILE *checkpoint_file;
+
+gboolean
+libslab_gtk_image_set_by_id (GtkImage *image, const gchar *id)
+{
+	GdkPixbuf *pixbuf;
+
+	gint size;
+	gint width;
+	gint height;
+
+	GtkIconTheme *icon_theme;
+
+	gboolean found;
+
+	gchar *tmp;
+
+
+	if (! id)
+		return FALSE;
+
+	g_object_get (G_OBJECT (image), "icon-size", & size, NULL);
+
+	if (size == GTK_ICON_SIZE_INVALID)
+		size = GTK_ICON_SIZE_DND;
+
+	gtk_icon_size_lookup (size, & width, & height);
+
+	if (g_path_is_absolute (id)) {
+		pixbuf = gdk_pixbuf_new_from_file_at_size (id, width, height, NULL);
+
+		found = (pixbuf != NULL);
+
+		if (found) {
+			gtk_image_set_from_pixbuf (image, pixbuf);
+
+			g_object_unref (pixbuf);
+		}
+		else
+			gtk_image_set_from_stock (image, "gtk-missing-image", size);
+	}
+	else {
+		tmp = g_strdup (id);
+
+		if ( /* file extensions are not copesetic with loading by "name" */
+			g_str_has_suffix (tmp, ".png") ||
+			g_str_has_suffix (tmp, ".svg") ||
+			g_str_has_suffix (tmp, ".xpm")
+		)
+
+			tmp [strlen (tmp) - 4] = '\0';
+
+		if (gtk_widget_has_screen (GTK_WIDGET (image)))
+			icon_theme = gtk_icon_theme_get_for_screen (
+				gtk_widget_get_screen (GTK_WIDGET (image)));
+		else
+			icon_theme = gtk_icon_theme_get_default ();
+
+		found = gtk_icon_theme_has_icon (icon_theme, tmp);
+
+		if (found)
+			gtk_image_set_from_icon_name (image, tmp, size);
+		else
+			gtk_image_set_from_stock (image, "gtk-missing-image", size);
+
+		g_free (tmp);
+	}
+
+	return found;
+}
+
+GnomeDesktopItem *
+libslab_gnome_desktop_item_new_from_unknown_id (const gchar *id)
+{
+	GnomeDesktopItem *item;
+	gchar            *basename;
+
+	GError *error = NULL;
+
+
+	if (! id)
+		return NULL;
+
+	item = gnome_desktop_item_new_from_uri (id, 0, & error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_file (id, 0, & error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_basename (id, 0, & error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	basename = g_strrstr (id, "/");
+
+	if (basename) {
+		basename++;
+
+		item = gnome_desktop_item_new_from_basename (basename, 0, &error);
+
+		if (! error)
+			return item;
+		else {
+			g_error_free (error);
+			error = NULL;
+		}
+	}
+
+	return NULL;
+}
+
+gboolean
+libslab_gnome_desktop_item_launch_default (GnomeDesktopItem *item)
+{
+	GError *error = NULL;
+
+	if (! item)
+		return FALSE;
+
+	gnome_desktop_item_launch (item, NULL, GNOME_DESKTOP_ITEM_LAUNCH_ONLY_ONE, & error);
+
+	if (error) {
+		g_warning ("error launching %s [%s]\n",
+			gnome_desktop_item_get_location (item), error->message);
+
+		g_error_free (error);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gchar *
+libslab_gnome_desktop_item_get_docpath (GnomeDesktopItem *item)
+{
+	gchar *path;
+
+	path = g_strdup (gnome_desktop_item_get_localestring (item, GNOME_DESKTOP_ITEM_DOC_PATH));
+
+	if (! path)
+		path = g_strdup (gnome_desktop_item_get_localestring (item, ALTERNATE_DOCPATH_KEY));
+
+	return path;
+}
+
+gboolean
+libslab_gnome_desktop_item_open_help (GnomeDesktopItem *item)
+{
+	gchar *doc_path;
+	gchar *help_uri;
+
+	GError *error = NULL;
+
+	gboolean retval = FALSE;
+
+
+	if (! item)
+		return retval;
+
+	doc_path = libslab_gnome_desktop_item_get_docpath (item);
+
+	if (doc_path) {
+		help_uri = g_strdup_printf ("ghelp:%s", doc_path);
+
+		gtk_show_uri (NULL, help_uri, gtk_get_current_event_time (), &error);
+
+		if (error) {
+			g_warning ("error opening %s [%s]\n", help_uri, error->message);
+
+			g_error_free (error);
+
+			retval = FALSE;
+		}
+		else
+			retval = TRUE;
+
+		g_free (help_uri);
+		g_free (doc_path);
+	}
+
+	return retval;
+}
+
+guint32
+libslab_get_current_time_millis ()
+{
+	GTimeVal t_curr;
+
+	g_get_current_time (& t_curr);
+
+	return 1000L * t_curr.tv_sec + t_curr.tv_usec / 1000L;
+}
+
+gint
+libslab_strcmp (const gchar *a, const gchar *b)
+{
+	if (! a && ! b)
+		return 0;
+
+	if (! a)
+		return strcmp ("", b);
+
+	if (! b)
+		return strcmp (a, "");
+
+	return strcmp (a, b);
+}
+
+gint
+libslab_strlen (const gchar *a)
+{
+	if (! a)
+		return 0;
+
+	return strlen (a);
+}
+
+gpointer
+libslab_get_gconf_value (const gchar *key)
+{
+	GConfClient *client;
+	GConfValue  *value;
+	GError      *error = NULL;
+
+	gpointer retval = NULL;
+
+	GList  *list;
+	GSList *slist;
+
+	GConfValue *value_i;
+	GSList     *node;
+
+
+	client = gconf_client_get_default ();
+	value  = gconf_client_get (client, key, & error);
+
+	if (error || ! value)
+		libslab_handle_g_error (& error, "%s: error getting %s", G_STRFUNC, key);
+	else {
+		switch (value->type) {
+			case GCONF_VALUE_STRING:
+				retval = (gpointer) g_strdup (gconf_value_get_string (value));
+				break;
+
+			case GCONF_VALUE_INT:
+				retval = GINT_TO_POINTER (gconf_value_get_int (value));
+				break;
+
+			case GCONF_VALUE_BOOL:
+				retval = GINT_TO_POINTER (gconf_value_get_bool (value));
+				break;
+
+			case GCONF_VALUE_LIST:
+				list = NULL;
+				slist = gconf_value_get_list (value);
+
+				for (node = slist; node; node = node->next) {
+					value_i = (GConfValue *) node->data;
+
+					if (value_i->type == GCONF_VALUE_STRING)
+						list = g_list_append (
+							list, g_strdup (
+								gconf_value_get_string (value_i)));
+					else if (value_i->type == GCONF_VALUE_INT)
+						list = g_list_append (
+							list, GINT_TO_POINTER (
+								gconf_value_get_int (value_i)));
+					else
+						;
+				}
+
+				retval = (gpointer) list;
+
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	g_object_unref (client);
+
+	if (value)
+		gconf_value_free (value);
+
+	return retval;
+}
+
+void
+libslab_set_gconf_value (const gchar *key, gconstpointer data)
+{
+	GConfClient *client;
+	GConfValue  *value;
+
+	GConfValueType type;
+	GConfValueType list_type;
+
+	GSList *slist = NULL;
+
+	GError *error = NULL;
+
+	GConfValue *value_i;
+	GList      *node;
+
+
+	client = gconf_client_get_default ();
+	value  = gconf_client_get (client, key, & error);
+
+	if (error) {
+		libslab_handle_g_error (&error, "%s: error getting %s", G_STRFUNC, key);
+
+		goto exit;
+	}
+
+	type = value->type;
+	list_type = ((type == GCONF_VALUE_LIST) ?
+		gconf_value_get_list_type (value) : GCONF_VALUE_INVALID);
+
+	gconf_value_free (value);
+	value = gconf_value_new (type);
+
+	if (type == GCONF_VALUE_LIST)
+		gconf_value_set_list_type (value, list_type);
+
+	switch (type) {
+		case GCONF_VALUE_STRING:
+			gconf_value_set_string (value, g_strdup ((gchar *) data));
+			break;
+
+		case GCONF_VALUE_INT:
+			gconf_value_set_int (value, GPOINTER_TO_INT (data));
+			break;
+
+		case GCONF_VALUE_BOOL:
+			gconf_value_set_bool (value, GPOINTER_TO_INT (data));
+			break;
+
+		case GCONF_VALUE_LIST:
+			for (node = (GList *) data; node; node = node->next) {
+				value_i = gconf_value_new (list_type);
+
+				if (list_type == GCONF_VALUE_STRING)
+					gconf_value_set_string (value_i, (const gchar *) node->data);
+				else if (list_type == GCONF_VALUE_INT)
+					gconf_value_set_int (value_i, GPOINTER_TO_INT (node->data));
+				else
+					g_assert_not_reached ();
+
+				slist = g_slist_append (slist, value_i);
+			}
+
+			gconf_value_set_list_nocopy (value, slist);
+
+			break;
+
+		default:
+			break;
+	}
+
+	gconf_client_set (client, key, value, & error);
+
+	if (error)
+		libslab_handle_g_error (&error, "%s: error setting %s", G_STRFUNC, key);
+
+exit:
+
+	gconf_value_free (value);
+	g_object_unref (client);
+}
+
+guint
+libslab_gconf_notify_add (const gchar *key, GConfClientNotifyFunc callback, gpointer user_data)
+{
+	GConfClient *client;
+	guint        conn_id;
+
+	GError *error = NULL;
+
+
+	client  = gconf_client_get_default ();
+	conn_id = gconf_client_notify_add (client, key, callback, user_data, NULL, & error);
+
+	if (error)
+		libslab_handle_g_error (
+			& error, "%s: error adding gconf notify for (%s)", G_STRFUNC, key);
+
+	g_object_unref (client);
+
+	return conn_id;
+}
+
+void
+libslab_gconf_notify_remove (guint conn_id)
+{
+	GConfClient *client;
+
+	GError *error = NULL;
+
+
+	if (conn_id == 0)
+		return;
+
+	client = gconf_client_get_default ();
+	gconf_client_notify_remove (client, conn_id);
+
+	if (error)
+		libslab_handle_g_error (
+			& error, "%s: error removing gconf notify", G_STRFUNC);
+
+	g_object_unref (client);
+}
+
+void
+libslab_handle_g_error (GError **error, const gchar *msg_format, ...)
+{
+	gchar   *msg;
+	va_list  args;
+
+
+	va_start (args, msg_format);
+	msg = g_strdup_vprintf (msg_format, args);
+	va_end (args);
+
+	if (*error) {
+		g_log (
+			G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+			"\nGError raised: [%s]\nuser_message: [%s]\n", (*error)->message, msg);
+
+		g_error_free (*error);
+
+		*error = NULL;
+	}
+	else
+		g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "\nerror raised: [%s]\n", msg);
+
+	g_free (msg);
+}
+
+gboolean
+libslab_desktop_item_is_a_terminal (const gchar *uri)
+{
+	GnomeDesktopItem *d_item;
+	const gchar      *categories;
+
+	gboolean is_terminal = FALSE;
+
+
+	d_item = libslab_gnome_desktop_item_new_from_unknown_id (uri);
+
+	if (! d_item)
+		return FALSE;
+
+	categories = gnome_desktop_item_get_string (d_item, GNOME_DESKTOP_ITEM_CATEGORIES);
+
+	is_terminal = (categories && strstr (categories, DESKTOP_ITEM_TERMINAL_EMULATOR_FLAG));
+
+	gnome_desktop_item_unref (d_item);
+
+	return is_terminal;
+}
+
+gboolean
+libslab_desktop_item_is_logout (const gchar *uri)
+{
+	GnomeDesktopItem *d_item;
+	gboolean is_logout = FALSE;
+
+
+	d_item = libslab_gnome_desktop_item_new_from_unknown_id (uri);
+
+	if (! d_item)
+		return FALSE;
+
+	is_logout = strstr ("Logout", gnome_desktop_item_get_string (d_item, GNOME_DESKTOP_ITEM_NAME)) != NULL;
+
+	gnome_desktop_item_unref (d_item);
+
+	return is_logout;
+}
+
+gboolean
+libslab_desktop_item_is_lockscreen (const gchar *uri)
+{
+	GnomeDesktopItem *d_item;
+	gboolean is_logout = FALSE;
+
+
+	d_item = libslab_gnome_desktop_item_new_from_unknown_id (uri);
+
+	if (! d_item)
+		return FALSE;
+
+	is_logout = strstr ("Lock Screen", gnome_desktop_item_get_string (d_item, GNOME_DESKTOP_ITEM_NAME)) != NULL;
+
+	gnome_desktop_item_unref (d_item);
+
+	return is_logout;
+}
+
+gchar *
+libslab_string_replace_once (const gchar *string, const gchar *key, const gchar *value)
+{
+	GString *str_built;
+	gint pivot;
+
+
+	pivot = strstr (string, key) - string;
+
+	str_built = g_string_new_len (string, pivot);
+	g_string_append (str_built, value);
+	g_string_append (str_built, & string [pivot + strlen (key)]);
+
+	return g_string_free (str_built, FALSE);
+}
+
+void
+libslab_spawn_command (const gchar *cmd)
+{
+	gchar **argv;
+
+	GError *error = NULL;
+
+
+	if (! cmd || strlen (cmd) < 1)
+		return;
+
+	argv = g_strsplit (cmd, " ", -1);
+
+	g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, & error);
+
+	if (error)
+		libslab_handle_g_error (& error, "%s: error spawning [%s]", G_STRFUNC, cmd);
+
+	g_strfreev (argv);
+}
+
+static guint thumbnail_factory_idle_id;
+static GnomeThumbnailFactory *thumbnail_factory;
+
+static void
+create_thumbnail_factory (void)
+{
+	/* The thumbnail_factory may already have been created by an applet
+	 * instance that was launched before the current one.
+	 */
+	if (thumbnail_factory != NULL)
+		return;
+
+	libslab_checkpoint ("create_thumbnail_factory(): start");
+
+	thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
+
+	libslab_checkpoint ("create_thumbnail_factory(): end");
+}
+
+static gboolean
+init_thumbnail_factory_idle_cb (gpointer data)
+{
+	create_thumbnail_factory ();
+	thumbnail_factory_idle_id = 0;
+	return FALSE;
+}
+
+void
+libslab_thumbnail_factory_preinit (void)
+{
+	thumbnail_factory_idle_id = g_idle_add (init_thumbnail_factory_idle_cb, NULL);
+}
+
+GnomeThumbnailFactory *
+libslab_thumbnail_factory_get (void)
+{
+	if (thumbnail_factory_idle_id != 0) {
+		g_source_remove (thumbnail_factory_idle_id);
+		thumbnail_factory_idle_id = 0;
+
+		create_thumbnail_factory ();
+	}
+
+	g_assert (thumbnail_factory != NULL);
+	return thumbnail_factory;
+}
+
+void
+libslab_checkpoint_init (const char *checkpoint_config_file_basename,
+			 const char *checkpoint_file_basename)
+{
+	char *filename;
+	struct stat st;
+	int result;
+	time_t t;
+	struct tm tm;
+	char *checkpoint_full_basename;
+
+	g_return_if_fail (checkpoint_config_file_basename != NULL);
+	g_return_if_fail (checkpoint_file_basename != NULL);
+
+	filename = g_build_filename (g_get_home_dir (), checkpoint_config_file_basename, NULL);
+
+	result = stat (filename, &st);
+	g_free (filename);
+
+	if (result != 0)
+		return;
+
+	t = time (NULL);
+	tm = *localtime (&t);
+
+	checkpoint_full_basename = g_strdup_printf ("%s-%04d-%02d-%02d-%02d-%02d-%02d.checkpoint",
+						    checkpoint_file_basename,
+						    tm.tm_year + 1900,
+						    tm.tm_mon + 1,
+						    tm.tm_mday,
+						    tm.tm_hour,
+						    tm.tm_min,
+						    tm.tm_sec);
+
+	filename = g_build_filename (g_get_home_dir (), checkpoint_full_basename, NULL);
+	g_free (checkpoint_full_basename);
+
+	checkpoint_file = fopen (filename, "w");
+	g_free (filename);
+}
+
+void
+libslab_checkpoint (const char *format, ...)
+{
+	va_list args;
+	struct timeval tv;
+	struct tm tm;
+	struct rusage rusage;
+
+	if (!checkpoint_file)
+		return;
+
+	gettimeofday (&tv, NULL);
+	tm = *localtime (&tv.tv_sec);
+
+	getrusage (RUSAGE_SELF, &rusage);
+
+	fprintf (checkpoint_file,
+		 "%02d:%02d:%02d.%04d (user:%d.%04d, sys:%d.%04d) - ",
+		 (int) tm.tm_hour,
+		 (int) tm.tm_min,
+		 (int) tm.tm_sec,
+		 (int) (tv.tv_usec / 100),
+		 (int) rusage.ru_utime.tv_sec,
+		 (int) (rusage.ru_utime.tv_usec / 100),
+		 (int) rusage.ru_stime.tv_sec,
+		 (int) (rusage.ru_stime.tv_usec / 100));
+
+	va_start (args, format);
+	vfprintf (checkpoint_file, format, args);
+	va_end (args);
+
+	fputs ("\n", checkpoint_file);
+	fflush (checkpoint_file);
+}
diff --git a/libslab/libslab-utils.h b/libslab/libslab-utils.h
new file mode 100644
index 0000000..ee0cdc8
--- /dev/null
+++ b/libslab/libslab-utils.h
@@ -0,0 +1,39 @@
+#ifndef __LIBSLAB_UTILS_H__
+#define __LIBSLAB_UTILS_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomeui/gnome-thumbnail.h>
+
+G_BEGIN_DECLS
+
+gboolean          libslab_gtk_image_set_by_id (GtkImage *image, const gchar *id);
+GnomeDesktopItem *libslab_gnome_desktop_item_new_from_unknown_id (const gchar *id);
+gboolean          libslab_gnome_desktop_item_launch_default (GnomeDesktopItem *item);
+gchar            *libslab_gnome_desktop_item_get_docpath (GnomeDesktopItem *item);
+gboolean          libslab_gnome_desktop_item_open_help (GnomeDesktopItem *item);
+guint32           libslab_get_current_time_millis (void);
+gint              libslab_strcmp (const gchar *a, const gchar *b);
+gint              libslab_strlen (const gchar *a);
+gpointer          libslab_get_gconf_value (const gchar *key);
+void              libslab_set_gconf_value (const gchar *key, gconstpointer data);
+guint             libslab_gconf_notify_add (const gchar *key, GConfClientNotifyFunc callback, gpointer user_data);
+void              libslab_gconf_notify_remove (guint conn_id);
+void              libslab_handle_g_error (GError **error, const gchar *msg_format, ...);
+gboolean          libslab_desktop_item_is_a_terminal (const gchar *uri);
+gboolean          libslab_desktop_item_is_logout (const gchar *uri);
+gboolean          libslab_desktop_item_is_lockscreen (const gchar *uri);
+gchar            *libslab_string_replace_once (const gchar *string, const gchar *key, const gchar *value);
+void              libslab_spawn_command (const gchar *cmd);
+
+void libslab_thumbnail_factory_preinit (void);
+GnomeThumbnailFactory *libslab_thumbnail_factory_get (void);
+
+void libslab_checkpoint_init (const char *checkpoint_config_file_basename, const char *checkpoint_file_basename);
+void libslab_checkpoint (const char *format, ...);
+
+G_END_DECLS
+
+#endif
diff --git a/libslab/libslab.pc.in b/libslab/libslab.pc.in
new file mode 100644
index 0000000..101fa0f
--- /dev/null
+++ b/libslab/libslab.pc.in
@@ -0,0 +1,12 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libslab
+Description: Beautiful App Slab
+Requires: glib-2.0 gobject-2.0 gtk+-2.0 gnome-desktop-2.0 libgnome-menu
+Requires.private: gdk-2.0 librsvg-2.0
+Version: @VERSION@
+Libs: -L${libdir} -lslab
+Cflags: -I${includedir}/slab
diff --git a/libslab/nameplate-tile.c b/libslab/nameplate-tile.c
new file mode 100644
index 0000000..3ea5f3e
--- /dev/null
+++ b/libslab/nameplate-tile.c
@@ -0,0 +1,291 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "nameplate-tile.h"
+
+static void nameplate_tile_class_init (NameplateTileClass *);
+static void nameplate_tile_init (NameplateTile *);
+static void nameplate_tile_finalize (GObject *);
+static void nameplate_tile_get_property (GObject *, guint, GValue *, GParamSpec *);
+static void nameplate_tile_set_property (GObject *, guint, const GValue *, GParamSpec *);
+static GObject *nameplate_tile_constructor (GType, guint, GObjectConstructParam *);
+
+static void nameplate_tile_drag_begin (GtkWidget *, GdkDragContext *);
+
+static void nameplate_tile_setup (NameplateTile *);
+
+typedef struct
+{
+	GtkContainer *image_ctnr;
+	GtkContainer *header_ctnr;
+	GtkContainer *subheader_ctnr;
+} NameplateTilePrivate;
+
+#define NAMEPLATE_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAMEPLATE_TILE_TYPE, NameplateTilePrivate))
+
+enum
+{
+	PROP_0,
+	PROP_NAMEPLATE_IMAGE,
+	PROP_NAMEPLATE_HEADER,
+	PROP_NAMEPLATE_SUBHEADER,
+};
+
+G_DEFINE_TYPE (NameplateTile, nameplate_tile, TILE_TYPE)
+
+GtkWidget *nameplate_tile_new (const gchar * uri, GtkWidget * image, GtkWidget * header,
+	GtkWidget * subheader)
+{
+	return GTK_WIDGET (
+		g_object_new (NAMEPLATE_TILE_TYPE,
+		"tile-uri",            uri,
+		"nameplate-image",     image,
+		"nameplate-header",    header,
+		"nameplate-subheader", subheader, 
+		NULL));
+}
+
+static void
+nameplate_tile_class_init (NameplateTileClass * this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
+
+	g_obj_class->constructor = nameplate_tile_constructor;
+	g_obj_class->get_property = nameplate_tile_get_property;
+	g_obj_class->set_property = nameplate_tile_set_property;
+	g_obj_class->finalize = nameplate_tile_finalize;
+
+	widget_class->drag_begin = nameplate_tile_drag_begin;
+
+	g_type_class_add_private (this_class, sizeof (NameplateTilePrivate));
+
+	g_object_class_install_property (g_obj_class, PROP_NAMEPLATE_IMAGE,
+		g_param_spec_object ("nameplate-image", "nameplate-image", "nameplate image",
+			GTK_TYPE_WIDGET, G_PARAM_READWRITE));
+
+	g_object_class_install_property (g_obj_class, PROP_NAMEPLATE_HEADER,
+		g_param_spec_object ("nameplate-header", "nameplate-header", "nameplate header",
+			GTK_TYPE_WIDGET, G_PARAM_READWRITE));
+
+	g_object_class_install_property (g_obj_class, PROP_NAMEPLATE_SUBHEADER,
+		g_param_spec_object ("nameplate-subheader", "nameplate-subheader",
+			"nameplate subheader", GTK_TYPE_WIDGET, G_PARAM_READWRITE));
+}
+
+static void
+nameplate_tile_init (NameplateTile * this)
+{
+}
+
+static GObject *
+nameplate_tile_constructor (GType type, guint n_param, GObjectConstructParam * param)
+{
+	GObject *g_obj =
+		(*G_OBJECT_CLASS (nameplate_tile_parent_class)->constructor) (type, n_param, param);
+
+	nameplate_tile_setup (NAMEPLATE_TILE (g_obj));
+
+	return g_obj;
+}
+
+static void
+nameplate_tile_finalize (GObject * g_object)
+{
+	NameplateTile *np_tile;
+	NameplateTilePrivate *priv;
+
+	np_tile = NAMEPLATE_TILE (g_object);
+	priv = NAMEPLATE_TILE_GET_PRIVATE (np_tile);
+
+	(*G_OBJECT_CLASS (nameplate_tile_parent_class)->finalize) (g_object);
+}
+
+static void
+nameplate_tile_get_property (GObject * g_object, guint prop_id, GValue * value,
+	GParamSpec * param_spec)
+{
+	char *tooltip;
+	NameplateTile *np_tile = NAMEPLATE_TILE (g_object);
+
+	switch (prop_id)
+	{
+	case PROP_NAMEPLATE_IMAGE:
+		g_value_set_object (value, np_tile->image);
+		break;
+
+	case PROP_NAMEPLATE_HEADER:
+		g_value_set_object (value, np_tile->header);
+		break;
+
+	case PROP_NAMEPLATE_SUBHEADER:
+		g_value_set_object (value, np_tile->subheader);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+nameplate_tile_set_property (GObject * g_object, guint prop_id, const GValue * value,
+	GParamSpec * param_spec)
+{
+	NameplateTile *this = NAMEPLATE_TILE (g_object);
+	NameplateTilePrivate *priv = NAMEPLATE_TILE_GET_PRIVATE (this);
+
+	GObject *widget_obj = NULL;
+
+	switch (prop_id) {
+		case PROP_NAMEPLATE_IMAGE:
+		case PROP_NAMEPLATE_HEADER:
+		case PROP_NAMEPLATE_SUBHEADER:
+			widget_obj = g_value_get_object (value);
+			break;
+		default:
+			break;
+	}
+
+	switch (prop_id)
+	{
+	case PROP_NAMEPLATE_IMAGE:
+		if (GTK_IS_WIDGET (widget_obj))
+		{
+			if (GTK_IS_WIDGET (this->image))
+				gtk_widget_destroy (this->image);
+
+			this->image = GTK_WIDGET (widget_obj);
+
+			gtk_container_add (priv->image_ctnr, this->image);
+
+			gtk_widget_show_all (this->image);
+		}
+		else if (GTK_IS_WIDGET (this->image))
+			gtk_widget_destroy (this->image);
+
+		break;
+
+	case PROP_NAMEPLATE_HEADER:
+		if (GTK_IS_WIDGET (widget_obj))
+		{
+			if (GTK_IS_WIDGET (this->header))
+				gtk_widget_destroy (this->header);
+
+			this->header = GTK_WIDGET (widget_obj);
+
+			gtk_container_add (priv->header_ctnr, this->header);
+
+			gtk_widget_show_all (this->header);
+		}
+		else if (GTK_IS_WIDGET (this->header))
+			gtk_widget_destroy (this->header);
+
+		break;
+
+	case PROP_NAMEPLATE_SUBHEADER:
+		if (GTK_IS_WIDGET (widget_obj))
+		{
+			if (GTK_IS_WIDGET (this->subheader))
+				gtk_widget_destroy (this->subheader);
+
+			this->subheader = GTK_WIDGET (widget_obj);
+
+			gtk_container_add (priv->subheader_ctnr, this->subheader);
+
+			gtk_widget_show_all (this->subheader);
+		}
+		else if (GTK_IS_WIDGET (this->subheader))
+			gtk_widget_destroy (this->subheader);
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+nameplate_tile_setup (NameplateTile *this)
+{
+	NameplateTilePrivate *priv = NAMEPLATE_TILE_GET_PRIVATE (this);
+
+	GtkWidget *hbox;
+	GtkWidget *alignment;
+	GtkWidget *vbox;
+
+	priv->image_ctnr = GTK_CONTAINER (gtk_alignment_new (0.5, 0.5, 1.0, 1.0));
+	priv->header_ctnr = GTK_CONTAINER (gtk_alignment_new (0.0, 0.5, 1.0, 1.0));
+	priv->subheader_ctnr = GTK_CONTAINER (gtk_alignment_new (0.0, 0.5, 1.0, 1.0));
+
+	hbox = gtk_hbox_new (FALSE, 6);
+	vbox = gtk_vbox_new (FALSE, 0);
+
+	alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+
+	gtk_container_add (GTK_CONTAINER (this), hbox);
+	gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (priv->image_ctnr), FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0);
+
+	gtk_container_add (GTK_CONTAINER (alignment), vbox);
+	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->header_ctnr), FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->subheader_ctnr), FALSE, FALSE, 0);
+
+	if (GTK_IS_WIDGET (this->image))
+		gtk_container_add (priv->image_ctnr, this->image);
+
+	if (GTK_IS_WIDGET (this->header))
+		gtk_container_add (priv->header_ctnr, this->header);
+
+	if (GTK_IS_WIDGET (this->subheader))
+		gtk_container_add (priv->subheader_ctnr, this->subheader);
+
+	gtk_button_set_focus_on_click (GTK_BUTTON (this), FALSE);
+}
+
+static void
+nameplate_tile_drag_begin (GtkWidget * widget, GdkDragContext * context)
+{
+	NameplateTile *this = NAMEPLATE_TILE (widget);
+	GtkImage *image;
+
+	(*GTK_WIDGET_CLASS (nameplate_tile_parent_class)->drag_begin) (widget, context);
+
+	if (!this->image || !GTK_IS_IMAGE (this->image))
+		return;
+
+	image = GTK_IMAGE (this->image);
+
+	switch (image->storage_type)
+	{
+	case GTK_IMAGE_PIXBUF:
+		if (image->data.pixbuf.pixbuf)
+			gtk_drag_set_icon_pixbuf (context, image->data.pixbuf.pixbuf, 0, 0);
+
+		break;
+
+	case GTK_IMAGE_ICON_NAME:
+		if (image->data.name.pixbuf)
+			gtk_drag_set_icon_pixbuf (context, image->data.name.pixbuf, 0, 0);
+
+		break;
+
+	default:
+		break;
+	}
+}
diff --git a/libslab/nameplate-tile.h b/libslab/nameplate-tile.h
new file mode 100644
index 0000000..c0b8c54
--- /dev/null
+++ b/libslab/nameplate-tile.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NAMEPLATE_TILE_H__
+#define __NAMEPLATE_TILE_H__
+
+#include "tile.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NAMEPLATE_TILE_TYPE         (nameplate_tile_get_type ())
+#define NAMEPLATE_TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), NAMEPLATE_TILE_TYPE, NameplateTile))
+#define NAMEPLATE_TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), NAMEPLATE_TILE_TYPE, NameplateTileClass))
+#define IS_NAMEPLATE_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), NAMEPLATE_TILE_TYPE))
+#define IS_NAMEPLATE_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), NAMEPLATE_TILE_TYPE))
+#define NAMEPLATE_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NAMEPLATE_TILE_TYPE, NameplateTileClass))
+
+typedef struct {
+	Tile tile;
+
+	GtkWidget *image;
+	GtkWidget *header;
+	GtkWidget *subheader;
+} NameplateTile;
+
+typedef struct {
+	TileClass tile_class;
+} NameplateTileClass;
+
+GType nameplate_tile_get_type (void);
+
+GtkWidget *nameplate_tile_new (const gchar * uri, GtkWidget * image, GtkWidget * header,
+	GtkWidget * subheader);
+
+G_END_DECLS
+#endif
diff --git a/libslab/nld-marshal.list b/libslab/nld-marshal.list
new file mode 100644
index 0000000..158cf7a
--- /dev/null
+++ b/libslab/nld-marshal.list
@@ -0,0 +1 @@
+VOID:INT,STRING
diff --git a/libslab/recent-files.c b/libslab/recent-files.c
new file mode 100644
index 0000000..88539e7
--- /dev/null
+++ b/libslab/recent-files.c
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "recent-files.h"
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+G_DEFINE_TYPE (MainMenuRecentMonitor, main_menu_recent_monitor, G_TYPE_OBJECT)
+G_DEFINE_TYPE (MainMenuRecentFile,    main_menu_recent_file,    G_TYPE_OBJECT)
+
+#define MAIN_MENU_RECENT_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MAIN_MENU_RECENT_MONITOR_TYPE, MainMenuRecentMonitorPrivate))
+#define MAIN_MENU_RECENT_FILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MAIN_MENU_RECENT_FILE_TYPE, MainMenuRecentFilePrivate))
+
+typedef struct {
+	GFileMonitor *monitor;
+
+	gulong changed_handler_id;
+} MainMenuRecentMonitorPrivate;
+
+typedef struct {
+	GtkRecentInfo *item_obj;
+} MainMenuRecentFilePrivate;
+
+enum {
+	STORE_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint monitor_signals [LAST_SIGNAL] = { 0 };
+
+static void main_menu_recent_monitor_finalize (GObject *);
+static void main_menu_recent_file_finalize    (GObject *);
+
+static GList *get_files (MainMenuRecentMonitor *, gboolean);
+
+static void
+recent_file_store_monitor_cb (GFileMonitor *, GFile *,
+                              GFile *, GFileMonitorEvent, gpointer);
+
+static gint recent_item_mru_comp_func (gconstpointer, gconstpointer);
+
+static void
+main_menu_recent_monitor_class_init (MainMenuRecentMonitorClass *this_class)
+{
+	G_OBJECT_CLASS (this_class)->finalize = main_menu_recent_monitor_finalize;
+
+	this_class->changed = NULL;
+
+	g_type_class_add_private (this_class, sizeof (MainMenuRecentMonitorPrivate));
+
+	monitor_signals [STORE_CHANGED] = g_signal_new (
+		"changed", G_TYPE_FROM_CLASS (this_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (MainMenuRecentMonitorClass, changed),
+		NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void
+main_menu_recent_file_class_init (MainMenuRecentFileClass *this_class)
+{
+	G_OBJECT_CLASS (this_class)->finalize = main_menu_recent_file_finalize;
+
+	g_type_class_add_private (this_class, sizeof (MainMenuRecentFilePrivate));
+}
+
+static void
+main_menu_recent_monitor_init (MainMenuRecentMonitor *this)
+{
+	MainMenuRecentMonitorPrivate *priv = MAIN_MENU_RECENT_MONITOR_GET_PRIVATE (this);
+
+	priv->model = NULL;
+
+	priv->changed_handler_id = 0;
+}
+
+static void
+main_menu_recent_file_init (MainMenuRecentFile *this)
+{
+	MainMenuRecentFilePrivate *priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (this);
+
+	priv->item_obj = NULL;
+
+	priv->uri       = NULL;
+	priv->mime_type = NULL;
+}
+
+static void
+main_menu_recent_monitor_finalize (GObject *g_object)
+{
+	MainMenuRecentMonitorPrivate *priv = MAIN_MENU_RECENT_MONITOR_GET_PRIVATE (g_object);
+
+	g_file_monitor_cancel (priv->monitor);
+	g_object_unref (priv->monitor);
+
+	(* G_OBJECT_CLASS (main_menu_recent_monitor_parent_class)->finalize) (g_object);
+}
+
+static void
+main_menu_recent_file_finalize (GObject *g_object)
+{
+	MainMenuRecentFilePrivate *priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (g_object);
+
+	if (priv->item_obj)
+		gtk_recent_info_unref (priv->item_obj);
+
+	(* G_OBJECT_CLASS (main_menu_recent_file_parent_class)->finalize) (g_object);
+}
+
+MainMenuRecentMonitor *
+main_menu_recent_monitor_new ()
+{
+	MainMenuRecentMonitor *this = g_object_new (MAIN_MENU_RECENT_MONITOR_TYPE, NULL);
+	MainMenuRecentMonitorPrivate *priv = MAIN_MENU_RECENT_MONITOR_GET_PRIVATE (this);
+
+	GtkRecentManager *manager;
+	gchar *store_path;
+	GFile *store_file;
+
+
+	manager = gtk_recent_manager_get_default ();
+	g_object_get (G_OBJECT (manager), "filename", & store_path, NULL);
+	store_file = g_file_new_for_path (store_path);
+
+	priv->monitor = g_file_monitor_file (store_file,
+					     0, NULL, NULL);
+	if (priv->monitor) {
+		g_signal_connect (priv->monitor, "changed",
+				  G_CALLBACK (recent_file_store_monitor_cb),
+				  this);
+	}
+
+	g_free (store_path);
+	g_object_unref (store_file);
+
+	return this;
+}
+
+static GList *
+get_files (MainMenuRecentMonitor *this, gboolean apps)
+{
+	GList *list;
+	GList *items;
+
+	GtkRecentManager *manager = gtk_recent_manager_get_default ();
+
+	GtkRecentInfo *info;
+
+	gboolean include;
+
+	MainMenuRecentFile *item;
+	MainMenuRecentFilePrivate *item_priv;
+
+	GList *node;
+
+
+	list = gtk_recent_manager_get_items (manager);
+
+	items = NULL;
+
+	for (node = list; node; node = node->next) {
+		item = g_object_new (MAIN_MENU_RECENT_FILE_TYPE, NULL);
+		item_priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (item);
+
+		info = (GtkRecentInfo *) node->data;
+
+		include = (apps && gtk_recent_info_has_group (info, "recently-used-apps")) ||
+			(! apps && ! gtk_recent_info_get_private_hint (info));
+
+		if (include) {
+			item_priv->item_obj = info;
+
+			items = g_list_insert_sorted (items, item, recent_item_mru_comp_func);
+		}
+		else
+			g_object_unref (item);
+	}
+
+	g_list_free (list);
+
+	return items;
+}
+
+GList *
+main_menu_get_recent_files (MainMenuRecentMonitor *this)
+{
+	return get_files (this, FALSE);
+}
+
+
+GList *
+main_menu_get_recent_apps (MainMenuRecentMonitor *this)
+{
+	return get_files (this, TRUE);
+}
+
+const gchar *
+main_menu_recent_file_get_uri (MainMenuRecentFile *this)
+{
+	MainMenuRecentFilePrivate *priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (this);
+
+	return gtk_recent_info_get_uri (priv->item_obj);
+}
+
+const gchar *
+main_menu_recent_file_get_mime_type (MainMenuRecentFile *this)
+{
+	MainMenuRecentFilePrivate *priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (this);
+
+	return gtk_recent_info_get_mime_type (priv->item_obj);
+}
+
+time_t
+main_menu_recent_file_get_modified (MainMenuRecentFile *this)
+{
+	MainMenuRecentFilePrivate *priv = MAIN_MENU_RECENT_FILE_GET_PRIVATE (this);
+
+	return gtk_recent_info_get_modified (priv->item_obj);
+}
+
+void
+main_menu_rename_recent_file (MainMenuRecentMonitor *this, const gchar *uri_0, const gchar *uri_1)
+{
+	GtkRecentManager *manager;
+
+	GError *error = NULL;
+
+
+	manager = gtk_recent_manager_get_default ();
+
+	gtk_recent_manager_move_item (manager, uri_0, uri_1, & error);
+
+	if (error) {
+		g_warning ("unable to update recent file store with renamed file, [%s] -> [%s]\n", uri_0, uri_1);
+
+		g_error_free (error);
+	}
+}
+
+void
+main_menu_remove_recent_file (MainMenuRecentMonitor *this, const gchar *uri)
+{
+	GtkRecentManager *manager;
+
+	GError *error = NULL;
+
+
+	manager = gtk_recent_manager_get_default ();
+
+	gtk_recent_manager_remove_item (manager, uri, & error);
+
+	if (error) {
+		g_warning ("unable to update recent file store with removed file, [%s]\n", uri);
+
+		g_error_free (error);
+	}
+}
+
+static void
+recent_file_store_monitor_cb (
+	GFileMonitor *mon, GFile *f1,
+	GFile *f2, GFileMonitorEvent event_type, gpointer user_data)
+{
+	g_signal_emit ((MainMenuRecentMonitor *) user_data, monitor_signals [STORE_CHANGED], 0);
+}
+
+static gint
+recent_item_mru_comp_func (gconstpointer a, gconstpointer b)
+{
+	time_t modified_a, modified_b;
+
+
+	modified_a = main_menu_recent_file_get_modified ((MainMenuRecentFile *) a);
+	modified_b = main_menu_recent_file_get_modified ((MainMenuRecentFile *) b);
+
+	return modified_b - modified_a;
+}
diff --git a/libslab/recent-files.h b/libslab/recent-files.h
new file mode 100644
index 0000000..36e60fa
--- /dev/null
+++ b/libslab/recent-files.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __RECENT_FILES_H__
+#define __RECENT_FILES_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <time.h>
+
+G_BEGIN_DECLS
+
+#define MAIN_MENU_RECENT_MONITOR_TYPE         (main_menu_recent_monitor_get_type ())
+#define MAIN_MENU_RECENT_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), MAIN_MENU_RECENT_MONITOR_TYPE, MainMenuRecentMonitor))
+#define MAIN_MENU_RECENT_MONITOR_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), MAIN_MENU_RECENT_MONITOR_TYPE, MainMenuRecentMonitorClass))
+#define IS_MAIN_MENU_RECENT_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MAIN_MENU_RECENT_MONITOR_TYPE))
+#define IS_MAIN_MENU_RECENT_MONITOR_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), MAIN_MENU_RECENT_MONITOR_TYPE))
+#define MAIN_MENU_RECENT_MONITOR_GET_CLASS(o) (G_TYPE_CHECK_GET_CLASS ((o), MAIN_MENU_RECENT_MONITOR_TYPE, MainMenuRecentMonitorClass))
+
+#define MAIN_MENU_RECENT_FILE_TYPE         (main_menu_recent_file_get_type ())
+#define MAIN_MENU_RECENT_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), MAIN_MENU_RECENT_FILE_TYPE, MainMenuRecentFile))
+#define MAIN_MENU_RECENT_FILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), MAIN_MENU_RECENT_FILE_TYPE, MainMenuRecentFileClass))
+#define IS_MAIN_MENU_RECENT_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MAIN_MENU_RECENT_FILE_TYPE))
+#define IS_MAIN_MENU_RECENT_FILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), MAIN_MENU_RECENT_FILE_TYPE))
+#define MAIN_MENU_RECENT_FILE_GET_CLASS(o) (G_TYPE_CHECK_GET_CLASS ((o), MAIN_MENU_RECENT_FILE_TYPE, MainMenuRecentFileClass))
+
+typedef struct {
+	GObject g_object;
+} MainMenuRecentMonitor;
+
+typedef struct {
+	GObjectClass g_object_class;
+
+	void (* changed) (MainMenuRecentMonitor *);
+} MainMenuRecentMonitorClass;
+
+GType main_menu_recent_monitor_get_type (void);
+
+MainMenuRecentMonitor *main_menu_recent_monitor_new (void);
+
+typedef struct {
+	GObject g_object;
+} MainMenuRecentFile;
+
+typedef struct {
+	GObjectClass g_object_class;
+} MainMenuRecentFileClass;
+
+GType main_menu_recent_file_get_type (void);
+
+GList *main_menu_get_recent_files (MainMenuRecentMonitor *mgr);
+GList *main_menu_get_recent_apps  (MainMenuRecentMonitor *mgr);
+
+const gchar *main_menu_recent_file_get_uri       (MainMenuRecentFile *this);
+const gchar *main_menu_recent_file_get_mime_type (MainMenuRecentFile *this);
+time_t       main_menu_recent_file_get_modified  (MainMenuRecentFile *this);
+
+void main_menu_rename_recent_file (MainMenuRecentMonitor *mgr, const gchar *uri_0, const gchar *uri_1);
+void main_menu_remove_recent_file (MainMenuRecentMonitor *mgr, const gchar *uri);
+
+G_END_DECLS
+
+#endif /* __RECENT_FILES_H__ */
diff --git a/libslab/search-bar.c b/libslab/search-bar.c
new file mode 100644
index 0000000..afb2366
--- /dev/null
+++ b/libslab/search-bar.c
@@ -0,0 +1,361 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "search-bar.h"
+#include "search-entry.h"
+#include "search-context-picker.h"
+#include "nld-marshal.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtkhbox.h>
+
+typedef struct
+{
+	GtkWidget *hbox;
+	NldSearchContextPicker *context_picker;
+	GtkEntry *entry;
+	GtkWidget *button;
+
+	int search_timeout;
+	guint timeout_id;
+
+	gboolean block_signal;
+} NldSearchBarPrivate;
+
+#define NLD_SEARCH_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NLD_TYPE_SEARCH_BAR, NldSearchBarPrivate))
+
+static void nld_search_bar_class_init (NldSearchBarClass *);
+static void nld_search_bar_init (NldSearchBar *);
+static void nld_search_bar_finalize (GObject *);
+
+static gboolean nld_search_bar_focus (GtkWidget *, GtkDirectionType);
+static void nld_search_bar_grab_focus (GtkWidget *);
+
+enum
+{
+	SEARCH,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (NldSearchBar, nld_search_bar, GTK_TYPE_VBOX)
+
+static void emit_search (NldSearchBar * search_bar);
+static void emit_search_callback (GtkWidget * widget, gpointer search_bar);
+
+static void nld_search_bar_class_init (NldSearchBarClass * nld_search_bar_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (nld_search_bar_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (nld_search_bar_class);
+
+	object_class->finalize = nld_search_bar_finalize;
+	widget_class->focus = nld_search_bar_focus;
+	widget_class->grab_focus = nld_search_bar_grab_focus;
+
+	g_type_class_add_private (nld_search_bar_class, sizeof (NldSearchBarPrivate));
+
+	signals[SEARCH] =
+		g_signal_new ("search", G_TYPE_FROM_CLASS (nld_search_bar_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (NldSearchBarClass, search),
+		NULL, NULL, nld_marshal_VOID__INT_STRING, G_TYPE_NONE, 2, G_TYPE_INT,
+		G_TYPE_STRING);
+}
+
+static void
+nld_search_bar_init (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+	GtkWidget *alignment;
+	GtkWidget *entry;
+
+	GTK_WIDGET_SET_FLAGS (search_bar, GTK_CAN_FOCUS);
+
+	priv->hbox = gtk_hbox_new (FALSE, 3);
+	gtk_box_pack_start (GTK_BOX (search_bar), priv->hbox, TRUE, FALSE, 0);
+
+	alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+	gtk_box_pack_start (GTK_BOX (priv->hbox), alignment, TRUE, TRUE, 0);
+
+	entry = nld_search_entry_new ();
+	priv->entry = GTK_ENTRY (entry);
+	gtk_widget_show (entry);
+	gtk_container_add (GTK_CONTAINER (alignment), entry);
+
+	g_signal_connect (entry, "activate", G_CALLBACK (emit_search_callback), search_bar);
+
+	priv->search_timeout = -1;
+}
+
+static void
+nld_search_bar_finalize (GObject * object)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (object);
+
+	if (priv->timeout_id)
+		g_source_remove (priv->timeout_id);
+
+	G_OBJECT_CLASS (nld_search_bar_parent_class)->finalize (object);
+}
+
+static gboolean
+nld_search_bar_focus (GtkWidget * widget, GtkDirectionType dir)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (widget);
+
+	return gtk_widget_child_focus (priv->hbox, dir);
+}
+
+gboolean
+nld_search_bar_has_focus (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	return GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (priv->entry));
+}
+
+static void
+nld_search_bar_grab_focus (GtkWidget * widget)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (widget);
+
+	gtk_widget_grab_focus (GTK_WIDGET (priv->entry));
+}
+
+GtkWidget *
+nld_search_bar_new (void)
+{
+	return g_object_new (NLD_TYPE_SEARCH_BAR, NULL);
+}
+
+void
+nld_search_bar_clear (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	priv->block_signal = TRUE;
+	gtk_entry_set_text (priv->entry, "");
+	if (priv->context_picker)
+		nld_search_context_picker_set_context (priv->context_picker, 0);
+	priv->block_signal = FALSE;
+}
+
+static void
+emit_search (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (priv->block_signal)
+		return;
+
+	if (priv->timeout_id)
+	{
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	g_signal_emit (search_bar, signals[SEARCH], 0, nld_search_bar_get_context_id (search_bar),
+		nld_search_bar_get_text (search_bar));
+}
+
+static void
+emit_search_callback (GtkWidget * widget, gpointer search_bar)
+{
+	emit_search (search_bar);
+}
+
+gboolean
+nld_search_bar_get_show_contexts (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	return priv->context_picker && GTK_WIDGET_VISIBLE (priv->context_picker);
+}
+
+static NldSearchContextPicker *
+build_context_picker (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+	GtkWidget *picker;
+
+	picker = nld_search_context_picker_new ();
+	g_signal_connect (picker, "context_changed", G_CALLBACK (emit_search_callback), search_bar);
+
+	gtk_box_pack_start (GTK_BOX (priv->hbox), picker, 0, 0, FALSE);
+	gtk_box_reorder_child (GTK_BOX (priv->hbox), picker, 0);
+
+	return NLD_SEARCH_CONTEXT_PICKER (picker);
+}
+
+void
+nld_search_bar_set_show_contexts (NldSearchBar * search_bar, gboolean show_contexts)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (show_contexts)
+	{
+		if (!priv->context_picker)
+			priv->context_picker = build_context_picker (search_bar);
+		gtk_widget_show (GTK_WIDGET (priv->context_picker));
+	}
+	else if (priv->context_picker)
+		gtk_widget_hide (GTK_WIDGET (priv->context_picker));
+}
+
+void
+nld_search_bar_add_context (NldSearchBar * search_bar, const char *label, const char *icon_name,
+	int context_id)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (!priv->context_picker)
+		priv->context_picker = build_context_picker (search_bar);
+
+	nld_search_context_picker_add_context (priv->context_picker, label, icon_name, context_id);
+}
+
+gboolean
+nld_search_bar_get_show_button (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	return priv->button != NULL;
+}
+
+void
+nld_search_bar_set_show_button (NldSearchBar * search_bar, gboolean show_button)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (show_button)
+	{
+		GtkWidget *image;
+
+		if (priv->button)
+			return;
+
+		priv->button = gtk_button_new_with_label (_("Find Now"));
+		image = gtk_image_new_from_icon_name ("system-search", GTK_ICON_SIZE_MENU);
+		gtk_button_set_image (GTK_BUTTON (priv->button), image);
+		gtk_widget_show (priv->button);
+
+		g_signal_connect (priv->button, "clicked", G_CALLBACK (emit_search_callback),
+			search_bar);
+
+		gtk_box_pack_end (GTK_BOX (priv->hbox), priv->button, FALSE, FALSE, 0);
+	}
+	else
+	{
+		if (!priv->button)
+			return;
+
+		gtk_widget_destroy (priv->button);
+		priv->button = NULL;
+	}
+}
+
+static gboolean
+search_timeout (gpointer search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	priv->timeout_id = 0;
+	emit_search (search_bar);
+	return FALSE;
+}
+
+static void
+entry_changed (GtkWidget * entry, gpointer search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (priv->search_timeout == 0)
+		emit_search (search_bar);
+	else if (priv->search_timeout > 0)
+	{
+		if (priv->timeout_id != 0)
+			g_source_remove (priv->timeout_id);
+		priv->timeout_id =
+			g_timeout_add (priv->search_timeout * 1000, search_timeout, search_bar);
+	}
+}
+
+int
+nld_search_bar_get_search_timeout (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	return priv->search_timeout;
+}
+
+void
+nld_search_bar_set_search_timeout (NldSearchBar * search_bar, int search_timeout)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (priv->search_timeout != -1 && search_timeout == -1)
+		g_signal_handlers_disconnect_by_func (priv->entry, entry_changed, search_bar);
+	else if (search_timeout != -1)
+	{
+		g_signal_connect (priv->entry, "changed", G_CALLBACK (entry_changed), search_bar);
+	}
+
+	priv->search_timeout = search_timeout;
+}
+
+const char *
+nld_search_bar_get_text (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	return gtk_entry_get_text (priv->entry);
+}
+
+void
+nld_search_bar_set_text (NldSearchBar * search_bar, const char *text, gboolean activate)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	gtk_entry_set_text (priv->entry, text);
+	if (activate)
+		emit_search (search_bar);
+}
+
+int
+nld_search_bar_get_context_id (NldSearchBar * search_bar)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	if (priv->context_picker && GTK_WIDGET_VISIBLE (priv->context_picker))
+		return nld_search_context_picker_get_context (priv->context_picker);
+	else
+		return -1;
+}
+
+void
+nld_search_bar_set_context_id (NldSearchBar * search_bar, int context_id)
+{
+	NldSearchBarPrivate *priv = NLD_SEARCH_BAR_GET_PRIVATE (search_bar);
+
+	g_return_if_fail (priv->context_picker != NULL);
+
+	nld_search_context_picker_set_context (priv->context_picker, context_id);
+}
diff --git a/libslab/search-bar.h b/libslab/search-bar.h
new file mode 100644
index 0000000..35a3a77
--- /dev/null
+++ b/libslab/search-bar.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NLD_SEARCH_BAR_H__
+#define __NLD_SEARCH_BAR_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NLD_TYPE_SEARCH_BAR            (nld_search_bar_get_type ())
+#define NLD_SEARCH_BAR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NLD_TYPE_SEARCH_BAR, NldSearchBar))
+#define NLD_SEARCH_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NLD_TYPE_SEARCH_BAR, NldSearchBarClass))
+#define NLD_IS_SEARCH_BAR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NLD_TYPE_SEARCH_BAR))
+#define NLD_IS_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NLD_TYPE_SEARCH_BAR))
+#define NLD_SEARCH_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NLD_TYPE_SEARCH_BAR, NldSearchBarClass))
+
+typedef struct
+{
+	GtkVBox parent;
+} NldSearchBar;
+
+typedef struct
+{
+	GtkVBoxClass parent_class;
+
+	void (*search) (NldSearchBar *, int context_id, const char *text);
+} NldSearchBarClass;
+
+GType nld_search_bar_get_type (void);
+
+GtkWidget *nld_search_bar_new (void);
+
+void nld_search_bar_clear (NldSearchBar * search_bar);
+gboolean nld_search_bar_has_focus (NldSearchBar * search_bar);
+
+gboolean nld_search_bar_get_show_contexts (NldSearchBar * search_bar);
+void nld_search_bar_set_show_contexts (NldSearchBar * search_bar, gboolean show_contexts);
+void nld_search_bar_add_context (NldSearchBar * search_bar, const char *label,
+	const char *icon_name, int context_id);
+
+gboolean nld_search_bar_get_show_button (NldSearchBar * search_bar);
+void nld_search_bar_set_show_button (NldSearchBar * search_bar, gboolean show_button);
+
+int nld_search_bar_get_search_timeout (NldSearchBar * search_bar);
+void nld_search_bar_set_search_timeout (NldSearchBar * search_bar, int search_timeout);
+
+const char *nld_search_bar_get_text (NldSearchBar * search_bar);
+void nld_search_bar_set_text (NldSearchBar * search_bar, const char *text, gboolean activate);
+
+int nld_search_bar_get_context_id (NldSearchBar * search_bar);
+void nld_search_bar_set_context_id (NldSearchBar * search_bar, int context_id);
+
+G_END_DECLS
+#endif /* __NLD_SEARCH_BAR_H__ */
diff --git a/libslab/search-context-picker.c b/libslab/search-context-picker.c
new file mode 100644
index 0000000..449c629
--- /dev/null
+++ b/libslab/search-context-picker.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "search-context-picker.h"
+
+#include <gtk/gtk.h>
+
+typedef struct
+{
+	GtkImage *cur_icon;
+	int cur_context;
+	GtkWidget *menu;
+} NldSearchContextPickerPrivate;
+
+#define NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NLD_TYPE_SEARCH_CONTEXT_PICKER, NldSearchContextPickerPrivate))
+
+enum
+{
+	CONTEXT_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void nld_search_context_picker_class_init (NldSearchContextPickerClass *);
+static void nld_search_context_picker_init (NldSearchContextPicker *);
+
+static void nld_search_context_picker_clicked (GtkButton *);
+
+G_DEFINE_TYPE (NldSearchContextPicker, nld_search_context_picker, GTK_TYPE_BUTTON)
+
+static void nld_search_context_picker_class_init (NldSearchContextPickerClass *
+	nld_search_context_picker_class)
+{
+	GtkButtonClass *button_class = GTK_BUTTON_CLASS (nld_search_context_picker_class);
+
+	button_class->clicked = nld_search_context_picker_clicked;
+
+	g_type_class_add_private (nld_search_context_picker_class,
+		sizeof (NldSearchContextPickerPrivate));
+
+	signals[CONTEXT_CHANGED] = g_signal_new ("context-changed",
+		G_TYPE_FROM_CLASS (nld_search_context_picker_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (NldSearchContextPickerClass, context_changed),
+		NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void
+nld_search_context_picker_init (NldSearchContextPicker * picker)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (picker);
+	GtkBox *hbox;
+
+	hbox = GTK_BOX (gtk_hbox_new (FALSE, 10));
+	gtk_container_add (GTK_CONTAINER (picker), GTK_WIDGET (hbox));
+
+	priv->cur_icon = GTK_IMAGE (gtk_image_new ());
+	gtk_box_pack_start (hbox, GTK_WIDGET (priv->cur_icon), FALSE, FALSE, 0);
+	gtk_box_pack_start (hbox, gtk_vseparator_new (), FALSE, FALSE, 0);
+	gtk_box_pack_start (hbox, gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE), FALSE, FALSE, 0);
+
+	gtk_widget_show_all (GTK_WIDGET (hbox));
+
+	priv->cur_context = -1;
+
+	priv->menu = gtk_menu_new ();
+}
+
+GtkWidget *
+nld_search_context_picker_new (void)
+{
+	return g_object_new (NLD_TYPE_SEARCH_CONTEXT_PICKER, NULL);
+}
+
+static void
+menu_position_func (GtkMenu * menu, int *x, int *y, gboolean * push_in, gpointer picker)
+{
+	GtkWidget *widget = GTK_WIDGET (picker);
+
+	gdk_window_get_origin (widget->window, x, y);
+	*x += widget->allocation.x;
+	*y += widget->allocation.y + widget->allocation.height;
+
+	if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+	{
+		GtkRequisition req;
+		gtk_widget_size_request (GTK_WIDGET (menu), &req);
+		*x += widget->allocation.width - req.width;
+	}
+
+	*push_in = FALSE;
+}
+
+static void
+nld_search_context_picker_clicked (GtkButton * button)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (button);
+
+	gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, menu_position_func, button, 1,
+		gtk_get_current_event_time ());
+}
+
+static void
+item_activated (GtkMenuItem * item, gpointer picker)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (picker);
+	GtkImage *image;
+	const char *icon_name;
+	GtkIconSize icon_size;
+
+	image = GTK_IMAGE (gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (item)));
+	gtk_image_get_icon_name (image, &icon_name, &icon_size);
+	gtk_image_set_from_icon_name (priv->cur_icon, icon_name, icon_size);
+
+	priv->cur_context =
+		GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item),
+			"NldSearchContextPicker:context_id"));
+	g_signal_emit (picker, signals[CONTEXT_CHANGED], 0);
+}
+
+void
+nld_search_context_picker_add_context (NldSearchContextPicker * picker, const char *label,
+	const char *icon_name, int context_id)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (picker);
+	GtkWidget *item = gtk_image_menu_item_new_with_label (label);
+	GtkWidget *image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+	GList *children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
+	gboolean first = children == NULL;
+
+	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+	g_object_set_data (G_OBJECT (item), "NldSearchContextPicker:context_id",
+		GINT_TO_POINTER (context_id));
+	g_signal_connect (item, "activate", G_CALLBACK (item_activated), picker);
+	gtk_widget_show_all (item);
+
+	gtk_container_add (GTK_CONTAINER (priv->menu), item);
+	if (first) {
+		item_activated (GTK_MENU_ITEM (item), picker);
+		g_list_free (children);
+	}
+}
+
+int
+nld_search_context_picker_get_context (NldSearchContextPicker * picker)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (picker);
+
+	return priv->cur_context;
+}
+
+void
+nld_search_context_picker_set_context (NldSearchContextPicker * picker, int context_id)
+{
+	NldSearchContextPickerPrivate *priv = NLD_SEARCH_CONTEXT_PICKER_GET_PRIVATE (picker);
+	GList *children;
+
+	children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
+	while (children)
+	{
+		GtkMenuItem *item = children->data;
+		int item_id =
+			GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item),
+				"NldSearchContextPicker:content_id"));
+
+		if (item_id == context_id)
+		{
+			item_activated (item, picker);
+			return;
+		}
+
+		children = children->next;
+	}
+	g_list_free (children);
+
+	priv->cur_context = -1;
+	g_signal_emit (picker, signals[CONTEXT_CHANGED], 0);
+}
diff --git a/libslab/search-context-picker.h b/libslab/search-context-picker.h
new file mode 100644
index 0000000..fb1411e
--- /dev/null
+++ b/libslab/search-context-picker.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NLD_SEARCH_CONTEXT_PICKER_H__
+#define __NLD_SEARCH_CONTEXT_PICKER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NLD_TYPE_SEARCH_CONTEXT_PICKER            (nld_search_context_picker_get_type ())
+#define NLD_SEARCH_CONTEXT_PICKER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NLD_TYPE_SEARCH_CONTEXT_PICKER, NldSearchContextPicker))
+#define NLD_SEARCH_CONTEXT_PICKER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NLD_TYPE_SEARCH_CONTEXT_PICKER, NldSearchContextPickerClass))
+#define NLD_IS_SEARCH_CONTEXT_PICKER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NLD_TYPE_SEARCH_CONTEXT_PICKER))
+#define NLD_IS_SEARCH_CONTEXT_PICKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NLD_TYPE_SEARCH_CONTEXT_PICKER))
+#define NLD_SEARCH_CONTEXT_PICKER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NLD_TYPE_SEARCH_CONTEXT_PICKER, NldSearchContextPickerClass))
+
+typedef struct
+{
+	GtkButton parent;
+} NldSearchContextPicker;
+
+typedef struct
+{
+	GtkButtonClass parent_class;
+
+	void (*context_changed) (NldSearchContextPicker *);
+} NldSearchContextPickerClass;
+
+GType nld_search_context_picker_get_type (void);
+
+GtkWidget *nld_search_context_picker_new (void);
+
+void nld_search_context_picker_add_context (NldSearchContextPicker * picker, const char *label,
+	const char *icon_name, int context_id);
+
+int nld_search_context_picker_get_context (NldSearchContextPicker * picker);
+void nld_search_context_picker_set_context (NldSearchContextPicker * picker, int context_id);
+
+G_END_DECLS
+#endif /* __NLD_SEARCH_CONTEXT_PICKER_H__ */
diff --git a/libslab/search-entry-watermark.svg b/libslab/search-entry-watermark.svg
new file mode 100644
index 0000000..f72efff
--- /dev/null
+++ b/libslab/search-entry-watermark.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="24"
+   height="16"
+   id="svg5418"
+   sodipodi:version="0.32"
+   inkscape:version="0.42+devel"
+   version="1.0"
+   sodipodi:docbase="/home/jimmac/Desktop"
+   sodipodi:docname="search-control-watermark.svg">
+  <defs
+     id="defs5420" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#d0d0d0"
+     borderopacity="1"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="16.668254"
+     inkscape:cy="7.6484815"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:showpageshadow="false"
+     inkscape:window-width="770"
+     inkscape:window-height="580"
+     inkscape:window-x="218"
+     inkscape:window-y="121" />
+  <metadata
+     id="metadata5423">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="opacity:0.15340911;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 3.4291453,1.1191527 C 2.6305762,2.3552041 2.1745042,3.8208445 2.1745042,5.400878 C 2.174511,9.770892 5.7107317,13.327038 10.080746,13.327038 C 11.948965,13.327031 13.605421,12.578488 14.959918,11.494855 C 14.849633,12.035463 14.910326,12.599456 15.358218,12.988478 L 17.807759,15.099467 L 22.627183,15.099467 L 17.429377,10.578766 C 17.423744,10.573876 17.415133,10.563637 17.409459,10.558855 C 17.055382,10.268755 16.617709,10.181311 16.194648,10.240213 C 17.261341,8.891261 18.006906,7.252857 18.006906,5.400878 C 18.006906,3.8201565 17.531556,2.355531 16.732353,1.1191527 L 14.481959,1.1191527 C 15.505805,2.208877 16.1349,3.6692927 16.1349,5.281383 C 16.134907,8.642939 13.402473,11.375366 10.040917,11.375366 C 6.679374,11.375366 3.9469402,8.642939 3.9469402,5.281383 C 3.9469402,3.6706415 4.5775542,2.2085978 5.5998813,1.1191527 L 3.4291453,1.1191527 z "
+       id="path5105" />
+  </g>
+</svg>
diff --git a/libslab/search-entry.c b/libslab/search-entry.c
new file mode 100644
index 0000000..1d17c94
--- /dev/null
+++ b/libslab/search-entry.c
@@ -0,0 +1,145 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "search-entry.h"
+#include "search-entry-watermark.h"
+
+#include <librsvg/rsvg.h>
+#include <string.h>
+
+typedef struct
+{
+	GdkPixbuf *watermark;
+	int width, height;
+} NldSearchEntryPrivate;
+
+#define NLD_SEARCH_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NLD_TYPE_SEARCH_ENTRY, NldSearchEntryPrivate))
+
+static void nld_search_entry_class_init (NldSearchEntryClass *);
+static void nld_search_entry_init (NldSearchEntry *);
+static void nld_search_entry_finalize (GObject *);
+
+static void nld_search_entry_realize (GtkWidget * widget);
+static gboolean nld_search_entry_expose_event (GtkWidget * widget, GdkEventExpose * event);
+
+G_DEFINE_TYPE (NldSearchEntry, nld_search_entry, GTK_TYPE_ENTRY)
+
+static void nld_search_entry_class_init (NldSearchEntryClass * nld_search_entry_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (nld_search_entry_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (nld_search_entry_class);
+
+	g_type_class_add_private (nld_search_entry_class, sizeof (NldSearchEntryPrivate));
+
+	widget_class->realize = nld_search_entry_realize;
+	widget_class->expose_event = nld_search_entry_expose_event;
+
+	g_obj_class->finalize = nld_search_entry_finalize;
+}
+
+static void
+nld_search_entry_init (NldSearchEntry * entry)
+{
+}
+
+static void
+nld_search_entry_finalize (GObject * object)
+{
+	NldSearchEntryPrivate *priv = NLD_SEARCH_ENTRY_GET_PRIVATE (object);
+
+	if (priv->watermark)
+		g_object_unref (priv->watermark);
+
+	G_OBJECT_CLASS (nld_search_entry_parent_class)->finalize (object);
+}
+
+static void
+rsvg_size_callback (int *width, int *height, gpointer user_data)
+{
+	NldSearchEntryPrivate *priv = user_data;
+
+	*width = priv->width = priv->height * (double) *width / (double) *height;
+	*height = priv->height;
+}
+
+static void
+nld_search_entry_realize (GtkWidget * widget)
+{
+	NldSearchEntryPrivate *priv = NLD_SEARCH_ENTRY_GET_PRIVATE (widget);
+	int height;
+	GdkColor *gdkcolor;
+	char *svg, color[7];
+	RsvgHandle *rsvg;
+
+	GTK_WIDGET_CLASS (nld_search_entry_parent_class)->realize (widget);
+
+	gdk_window_get_geometry (GTK_ENTRY (widget)->text_area, NULL, NULL, NULL, &height, NULL);
+
+	if (height - 2 == priv->height)
+		return;
+	priv->height = height - 2;
+
+	gdkcolor = &widget->style->fg[GTK_WIDGET_STATE (widget)];
+	snprintf (color, 6, "%02x%02x%02x", gdkcolor->red >> 8, gdkcolor->green >> 8,
+		gdkcolor->blue >> 8);
+	svg = g_strdup_printf (SEARCH_ENTRY_WATERMARK_SVG, color, color);
+
+	rsvg = rsvg_handle_new ();
+	rsvg_handle_set_size_callback (rsvg, rsvg_size_callback, priv, NULL);
+	rsvg_handle_write (rsvg, (const guchar *) svg, strlen (svg), NULL);
+	rsvg_handle_close (rsvg, NULL);
+	g_free (svg);
+
+	if (priv->watermark)
+		g_object_unref (priv->watermark);
+	priv->watermark = rsvg_handle_get_pixbuf (rsvg);
+	rsvg_handle_free (rsvg);
+}
+
+static gboolean
+nld_search_entry_expose_event (GtkWidget * widget, GdkEventExpose * event)
+{
+	NldSearchEntryPrivate *priv = NLD_SEARCH_ENTRY_GET_PRIVATE (widget);
+	GTK_WIDGET_CLASS (nld_search_entry_parent_class)->expose_event (widget, event);
+
+	if (event->window == GTK_ENTRY (widget)->text_area)
+	{
+		int width, height, x;
+
+		if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+		{
+			gdk_drawable_get_size (event->window, &width, &height);
+			x = width - priv->width - 1;
+		}
+		else
+			x = 1;
+		gdk_draw_pixbuf (event->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+			priv->watermark, 0, 0, x, 1, priv->width, priv->height,
+			GDK_RGB_DITHER_NORMAL, 0, 0);
+	}
+
+	return FALSE;
+}
+
+GtkWidget *
+nld_search_entry_new (void)
+{
+	return g_object_new (NLD_TYPE_SEARCH_ENTRY, NULL);
+}
diff --git a/libslab/search-entry.h b/libslab/search-entry.h
new file mode 100644
index 0000000..94188b8
--- /dev/null
+++ b/libslab/search-entry.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NLD_SEARCH_ENTRY_H__
+#define __NLD_SEARCH_ENTRY_H__
+
+#include <gtk/gtkentry.h>
+
+G_BEGIN_DECLS
+
+#define NLD_TYPE_SEARCH_ENTRY            (nld_search_entry_get_type ())
+#define NLD_SEARCH_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NLD_TYPE_SEARCH_ENTRY, NldSearchEntry))
+#define NLD_SEARCH_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NLD_TYPE_SEARCH_ENTRY, NldSearchEntryClass))
+#define NLD_IS_SEARCH_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NLD_TYPE_SEARCH_ENTRY))
+#define NLD_IS_SEARCH_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NLD_TYPE_SEARCH_ENTRY))
+#define NLD_SEARCH_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NLD_TYPE_SEARCH_ENTRY, NldSearchEntryClass))
+
+typedef struct
+{
+	GtkEntry parent;
+} NldSearchEntry;
+
+typedef struct
+{
+	GtkEntryClass parent_class;
+} NldSearchEntryClass;
+
+GType nld_search_entry_get_type (void);
+
+GtkWidget *nld_search_entry_new (void);
+
+G_END_DECLS
+#endif /* __NLD_SEARCH_ENTRY_H__ */
diff --git a/libslab/shell-window.c b/libslab/shell-window.c
new file mode 100644
index 0000000..4a9416c
--- /dev/null
+++ b/libslab/shell-window.c
@@ -0,0 +1,187 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "shell-window.h"
+
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtkbin.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtklayout.h>
+#include <gtk/gtkscrolledwindow.h>
+
+#include "app-resizer.h"
+
+static GtkWindowClass *parent_class = NULL;
+
+static void shell_window_class_init (ShellWindowClass *);
+static void shell_window_init (ShellWindow *);
+static void shell_window_destroy (GtkObject *);
+static void shell_window_handle_size_request (GtkWidget * widget, GtkRequisition * requisition,
+	AppShellData * data);
+
+gboolean shell_window_paint_window (GtkWidget * widget, GdkEventExpose * event, gpointer data);
+
+#define SHELL_WINDOW_BORDER_WIDTH 6
+
+GType
+shell_window_get_type (void)
+{
+	static GType object_type = 0;
+
+	if (!object_type)
+	{
+		static const GTypeInfo object_info = {
+			sizeof (ShellWindowClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) shell_window_class_init,
+			NULL,
+			NULL,
+			sizeof (ShellWindow),
+			0,
+			(GInstanceInitFunc) shell_window_init
+		};
+
+		object_type = g_type_register_static (
+			GTK_TYPE_FRAME, "ShellWindow", &object_info, 0);
+	}
+
+	return object_type;
+}
+
+static void
+shell_window_class_init (ShellWindowClass * klass)
+{
+	parent_class = g_type_class_peek_parent (klass);
+
+	((GtkObjectClass *) klass)->destroy = shell_window_destroy;
+}
+
+static void
+shell_window_init (ShellWindow * window)
+{
+	window->_hbox = NULL;
+	window->_left_pane = NULL;
+	window->_right_pane = NULL;
+}
+
+GtkWidget *
+shell_window_new (AppShellData * app_data)
+{
+	ShellWindow *window = g_object_new (SHELL_WINDOW_TYPE, NULL);
+
+	gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
+	gtk_frame_set_shadow_type(GTK_FRAME(window), GTK_SHADOW_NONE);
+
+	window->_hbox = GTK_BOX (gtk_hbox_new (FALSE, 0));
+	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (window->_hbox));
+
+	g_signal_connect (G_OBJECT (window), "expose-event", G_CALLBACK (shell_window_paint_window),
+		NULL);
+	window->resize_handler_id =
+		g_signal_connect (G_OBJECT (window), "size-request",
+		G_CALLBACK (shell_window_handle_size_request), app_data);
+
+	return GTK_WIDGET (window);
+}
+
+static void
+shell_window_destroy (GtkObject * obj)
+{
+}
+
+void
+shell_window_clear_resize_handler (ShellWindow * win)
+{
+	if (win->resize_handler_id)
+	{
+		g_signal_handler_disconnect (win, win->resize_handler_id);
+		win->resize_handler_id = 0;
+	}
+}
+
+/* We want the window to come up with proper runtime calculated width ( ie taking into account font size, locale, ...) so
+   we can't hard code a size. But since ScrolledWindow returns basically zero for it's size request we need to
+   grab the "real" desired width. Once it's shown though we want to allow the user to size down if they want too, so
+   we unhook this function
+*/
+static void
+shell_window_handle_size_request (GtkWidget * widget, GtkRequisition * requisition,
+	AppShellData * app_data)
+{
+	gint height;
+
+	/*
+	Fixme - counting on this being called after the real size request is done.
+	seems to be that way but I don't know why. I would think I would need to explictly call it here first
+	printf("Enter - shell_window_handle_size_request\n");
+	printf("passed in width:%d, height:%d\n", requisition->width, requisition->height);
+	printf("left side width:%d\n", SHELL_WINDOW(widget)->_left_pane->requisition.width);
+	printf("right side width:%d\n", GTK_WIDGET(APP_RESIZER(app_data->category_layout)->child)->requisition.width);
+	*/
+
+	requisition->width +=
+		GTK_WIDGET (APP_RESIZER (app_data->category_layout)->child)->requisition.width;
+
+	/* use the left side as a minimum height, if the right side is taller,
+	   use it up to SIZING_HEIGHT_PERCENT of the screen height
+	*/
+	height =
+		GTK_WIDGET (APP_RESIZER (app_data->category_layout)->child)->requisition.height +
+		10;
+	if (height > requisition->height)
+	{
+		requisition->height =
+			MIN (((gfloat) gdk_screen_height () * SIZING_HEIGHT_PERCENT), height);
+	}
+}
+
+void
+shell_window_set_contents (ShellWindow * shell, GtkWidget * left_pane, GtkWidget * right_pane)
+{
+	shell->_left_pane = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+	shell->_right_pane = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+
+	gtk_alignment_set_padding (GTK_ALIGNMENT (shell->_left_pane), 15, 15, 15, 15);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (shell->_right_pane), 0, 0, 0, 0);	/* space for vertical line */
+
+	gtk_box_pack_start (shell->_hbox, shell->_left_pane, FALSE, FALSE, 0);
+	gtk_box_pack_start (shell->_hbox, shell->_right_pane, TRUE, TRUE, 0);	/* this one takes any extra space */
+
+	gtk_container_add (GTK_CONTAINER (shell->_left_pane), left_pane);
+	gtk_container_add (GTK_CONTAINER (shell->_right_pane), right_pane);
+}
+
+gboolean
+shell_window_paint_window (GtkWidget * widget, GdkEventExpose * event, gpointer data)
+{
+	GtkWidget *left_pane, *right_pane;
+
+	left_pane = SHELL_WINDOW (widget)->_left_pane;
+	right_pane = SHELL_WINDOW (widget)->_right_pane;
+
+	/* draw left pane background */
+	gtk_paint_flat_box (widget->style, widget->window, widget->state, GTK_SHADOW_NONE, NULL, widget, "",
+		left_pane->allocation.x, left_pane->allocation.y, left_pane->allocation.width,
+		left_pane->allocation.height);
+
+	return FALSE;
+}
diff --git a/libslab/shell-window.h b/libslab/shell-window.h
new file mode 100644
index 0000000..92b58bc
--- /dev/null
+++ b/libslab/shell-window.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SHELL_WINDOW_H__
+#define __SHELL_WINDOW_H__
+
+#include <glib.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkframe.h>
+#include <libgnome/gnome-desktop-item.h>
+#include "app-shell.h"
+
+G_BEGIN_DECLS
+
+#define SHELL_WINDOW_TYPE            (shell_window_get_type ())
+#define SHELL_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_WINDOW_TYPE, ShellWindow))
+#define SHELL_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_WINDOW_TYPE, ShellWindowClass))
+#define IS_SHELL_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_WINDOW_TYPE))
+#define IS_SHELL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_WINDOW_TYPE))
+#define SHELL_WINDOW_GET_CLASS(obj)  (G_TYPE_CHECK_GET_CLASS ((obj), SHELL_WINDOW_TYPE, ShellWindowClass))
+
+typedef struct _ShellWindow ShellWindow;
+typedef struct _ShellWindowClass ShellWindowClass;
+
+struct _ShellWindow
+{
+	GtkFrame frame;
+
+	GtkBox *_hbox;
+	GtkWidget *_left_pane;
+	GtkWidget *_right_pane;
+
+	gulong resize_handler_id;
+};
+
+struct _ShellWindowClass
+{
+	GtkFrameClass parent_class;
+};
+
+GType shell_window_get_type (void);
+GtkWidget *shell_window_new (AppShellData * app_data);
+void shell_window_set_contents (ShellWindow * window, GtkWidget * left_pane,
+	GtkWidget * right_pane);
+void shell_window_clear_resize_handler (ShellWindow * win);
+
+G_END_DECLS
+#endif /* __SHELL_WINDOW_H__ */
diff --git a/libslab/slab-gnome-util.c b/libslab/slab-gnome-util.c
new file mode 100644
index 0000000..6c94c12
--- /dev/null
+++ b/libslab/slab-gnome-util.c
@@ -0,0 +1,474 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "slab-gnome-util.h"
+
+#include <gconf/gconf-client.h>
+#include <libgnome/gnome-url.h>
+#include <gio/gio.h>
+#include <string.h>
+
+gboolean
+get_slab_gconf_bool (const gchar * key)
+{
+	GConfClient *gconf_client;
+	GError *error;
+
+	gboolean value;
+
+	gconf_client = gconf_client_get_default ();
+	error = NULL;
+
+	value = gconf_client_get_bool (gconf_client, key, &error);
+
+	g_object_unref (gconf_client);
+
+	if (error)
+	{
+		g_warning ("error accessing %s [%s]\n", key, error->message);
+		g_error_free (error);
+	}
+
+	return value;
+}
+
+gint
+get_slab_gconf_int (const gchar * key)
+{
+	GConfClient *gconf_client;
+	GError *error;
+
+	gint value;
+
+	gconf_client = gconf_client_get_default ();
+	error = NULL;
+
+	value = gconf_client_get_int (gconf_client, key, &error);
+
+	g_object_unref (gconf_client);
+	if (error)
+	{
+		g_warning ("error accessing %s [%s]\n", key, error->message);
+		g_error_free (error);
+	}
+
+	return value;
+}
+
+gchar *
+get_slab_gconf_string (const gchar * key)
+{
+	GConfClient *gconf_client;
+	GError *error;
+
+	gchar *value;
+
+	gconf_client = gconf_client_get_default ();
+	error = NULL;
+
+	value = gconf_client_get_string (gconf_client, key, &error);
+
+	g_object_unref (gconf_client);
+	if (error)
+	{
+		g_warning ("error accessing %s [%s]\n", key, error->message);
+		g_error_free (error);
+	}
+
+	return value;
+}
+
+void
+free_list_of_strings (GList * string_list)
+{
+	g_assert (string_list != NULL);
+	g_list_foreach (string_list, (GFunc) g_free, NULL);
+	g_list_free (string_list);
+}
+
+void
+free_slab_gconf_slist_of_strings (GSList * string_list)
+{
+	g_assert (string_list != NULL);
+	g_slist_foreach (string_list, (GFunc) g_free, NULL);
+	g_slist_free (string_list);
+}
+
+GSList *
+get_slab_gconf_slist (const gchar * key)
+{
+	GConfClient *gconf_client;
+	GError *error;
+
+	GSList *value;
+
+	gconf_client = gconf_client_get_default ();
+	error = NULL;
+
+	value = gconf_client_get_list (gconf_client, key, GCONF_VALUE_STRING, &error);
+
+	g_object_unref (gconf_client);
+	if (error)
+	{
+		g_warning ("error accessing %s [%s]\n", key, error->message);
+
+		g_error_free (error);
+	}
+
+	return value;
+}
+
+GnomeDesktopItem *
+load_desktop_item_from_gconf_key (const gchar * key)
+{
+	GnomeDesktopItem *item;
+	gchar *id = get_slab_gconf_string (key);
+
+	if (!id)
+		return NULL;
+
+	item = load_desktop_item_from_unknown (id);
+	g_free (id);
+	return item;
+}
+
+GnomeDesktopItem *
+load_desktop_item_from_unknown (const gchar *id)
+{
+	GnomeDesktopItem *item;
+	gchar            *basename;
+
+	GError *error = NULL;
+
+
+	item = gnome_desktop_item_new_from_uri (id, 0, &error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_file (id, 0, &error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	item = gnome_desktop_item_new_from_basename (id, 0, &error);
+
+	if (! error)
+		return item;
+	else {
+		g_error_free (error);
+		error = NULL;
+	}
+
+	basename = g_strrstr (id, "/");
+
+	if (basename) {
+		basename++;
+
+		item = gnome_desktop_item_new_from_basename (basename, 0, &error);
+
+		if (! error)
+			return item;
+		else {
+			g_error_free (error);
+			error = NULL;
+		}
+	}
+
+	return NULL;
+}
+
+gchar *
+get_package_name_from_desktop_item (GnomeDesktopItem * desktop_item)
+{
+	gchar *argv[6];
+	gchar *package_name;
+	gint retval;
+	GError *error;
+
+	argv[0] = "rpm";
+	argv[1] = "-qf";
+	argv[2] = "--qf";
+	argv[3] = "%{NAME}";
+	argv[4] = g_filename_from_uri (gnome_desktop_item_get_location (desktop_item), NULL, NULL);
+	argv[5] = NULL;
+
+	error = NULL;
+
+	if (!g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &package_name, NULL,
+			&retval, &error))
+	{
+		g_warning ("error: [%s]\n", error->message);
+		g_error_free (error);
+		retval = -1;
+	}
+
+	g_free (argv[4]);
+
+	if (!retval)
+		return package_name;
+	else
+		return NULL;
+}
+
+gboolean
+open_desktop_item_exec (GnomeDesktopItem * desktop_item)
+{
+	GError *error = NULL;
+
+	if (!desktop_item)
+		return FALSE;
+
+	gnome_desktop_item_launch (desktop_item, NULL, GNOME_DESKTOP_ITEM_LAUNCH_ONLY_ONE, &error);
+
+	if (error)
+	{
+		g_warning ("error launching %s [%s]\n",
+			gnome_desktop_item_get_location (desktop_item), error->message);
+
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+open_desktop_item_help (GnomeDesktopItem * desktop_item)
+{
+	const gchar *doc_path;
+	gchar *help_uri;
+
+	GError *error;
+
+	if (!desktop_item)
+		return FALSE;
+
+	doc_path = gnome_desktop_item_get_string (desktop_item, "DocPath");
+
+	if (doc_path)
+	{
+		help_uri = g_strdup_printf ("ghelp:%s", doc_path);
+
+		error = NULL;
+
+		gtk_show_uri (NULL, help_uri, gtk_get_current_event_time (), &error);
+
+		if (error)
+		{
+			g_warning ("error opening %s [%s]\n", help_uri, error->message);
+
+			g_free (help_uri);
+			g_error_free (error);
+			return FALSE;
+		}
+
+		g_free (help_uri);
+	}
+	else
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+desktop_item_is_in_main_menu (GnomeDesktopItem * desktop_item)
+{
+	return desktop_uri_is_in_main_menu (gnome_desktop_item_get_location (desktop_item));
+}
+
+gboolean
+desktop_uri_is_in_main_menu (const gchar * uri)
+{
+	GSList *app_list;
+
+	GSList *node;
+	gint offset;
+	gint uri_len;
+	gboolean found = FALSE;
+
+	app_list = get_slab_gconf_slist (SLAB_USER_SPECIFIED_APPS_KEY);
+
+	if (!app_list)
+		return FALSE;
+
+	uri_len = strlen (uri);
+
+	for (node = app_list; node; node = node->next)
+	{
+		offset = uri_len - strlen ((gchar *) node->data);
+
+		if (offset < 0)
+			offset = 0;
+
+		if (!strcmp (&uri[offset], (gchar *) node->data))
+		{
+			found = TRUE;
+			break;
+		}
+	}
+
+	free_slab_gconf_slist_of_strings (app_list);
+	return found;
+}
+
+gint
+desktop_item_location_compare (gconstpointer a_obj, gconstpointer b_obj)
+{
+	const gchar *a;
+	const gchar *b;
+
+	gint offset;
+
+	a = (const gchar *) a_obj;
+	b = (const gchar *) b_obj;
+
+	offset = strlen (a) - strlen (b);
+
+	if (offset > 0)
+		return strcmp (&a[offset], b);
+	else if (offset < 0)
+		return strcmp (a, &b[-offset]);
+	else
+		return strcmp (a, b);
+}
+
+gboolean
+slab_load_image (GtkImage * image, GtkIconSize size, const gchar * image_id)
+{
+	GdkPixbuf *pixbuf;
+	gint width;
+	gint height;
+
+	gchar *id;
+
+	if (!image_id)
+		return FALSE;
+
+	id = g_strdup (image_id);
+
+	gtk_icon_size_lookup (size, &width, &height);
+
+	if (g_path_is_absolute (id))
+		pixbuf = gdk_pixbuf_new_from_file_at_size (id, width, height, NULL);
+	else
+	{
+		if (	/* file extensions are not copesetic with loading by "name" */
+			g_str_has_suffix (id, ".png") ||
+			g_str_has_suffix (id, ".svg") ||
+			g_str_has_suffix (id, ".xpm")
+		   )
+
+			id[strlen (id) - 4] = '\0';
+
+		pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), id, width, 0,
+			NULL);
+	}
+
+	if (pixbuf)
+	{
+		gtk_image_set_from_pixbuf (image, pixbuf);
+
+		g_object_unref (pixbuf);
+
+		g_free (id);
+
+		return TRUE;
+	}
+	else
+	{			/* This will make it show the "broken image" icon */
+		gtk_image_set_from_file (image, id);
+
+		g_free (id);
+
+		return FALSE;
+	}
+}
+
+gchar *
+string_replace_once (const gchar * str_template, const gchar * key, const gchar * value)
+{
+	GString *str_built;
+	gint pivot;
+
+	pivot = strstr (str_template, key) - str_template;
+
+	str_built = g_string_new_len (str_template, pivot);
+	g_string_append (str_built, value);
+	g_string_append (str_built, &str_template[pivot + strlen (key)]);
+
+	return g_string_free (str_built, FALSE);
+}
+
+void
+spawn_process (const gchar *command)
+{
+	gchar **argv;
+	GError *error = NULL;
+
+	if (!command || strlen (command) < 1)
+		return;
+
+	argv = g_strsplit (command, " ", -1);
+
+	g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
+
+	if (error)
+	{
+		g_warning ("error spawning [%s]: [%s]\n", command, error->message);
+
+		g_error_free (error);
+	}
+
+	g_strfreev (argv);
+}
+
+void
+copy_file (const gchar * src_uri, const gchar * dst_uri)
+{
+	GFile *src;
+	GFile *dst;
+	GError *error = NULL;
+	gboolean res;
+
+	src = g_file_new_for_uri (src_uri);
+	dst = g_file_new_for_uri (dst_uri);
+
+	res = g_file_copy (src, dst,
+			   G_FILE_COPY_NONE,
+			   NULL, NULL, NULL, &error);
+
+	if (!res)
+	{
+		g_warning ("error copying [%s] to [%s]: %s.", src_uri, dst_uri, error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (src);
+	g_object_unref (dst);
+}
diff --git a/libslab/slab-gnome-util.h b/libslab/slab-gnome-util.h
new file mode 100644
index 0000000..982f81e
--- /dev/null
+++ b/libslab/slab-gnome-util.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SLAB_GNOME_UTIL_H__
+#define __SLAB_GNOME_UTIL_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <libgnome/gnome-desktop-item.h>
+
+G_BEGIN_DECLS
+
+#define SLAB_APPLICATION_BROWSER_KEY    "/desktop/gnome/applications/main-menu/application_browser"
+#define SLAB_SYSTEM_LIST_KEY            "/desktop/gnome/applications/main-menu/system_list"
+#define SLAB_FILE_BROWSER_KEY           "/desktop/gnome/applications/main-menu/file_browser"
+#define SLAB_SYSTEM_MONITOR_KEY         "/desktop/gnome/applications/main-menu/system_monitor"
+#define SLAB_NETWORK_CONFIG_TOOL_KEY    "/desktop/gnome/applications/main-menu/network_config_tool"
+#define SLAB_NETWORK_CONFIG_TOOL_NM_KEY "/desktop/gnome/applications/main-menu/network_config_tool_nm"
+#define SLAB_URGENT_CLOSE_KEY           "/desktop/gnome/applications/main-menu/urgent_close"
+#define SLAB_LOCK_SCREEN_PRIORITY_KEY   "/desktop/gnome/applications/main-menu/lock_screen_priority"
+#define SLAB_MAIN_MENU_REORDERING_KEY   "/desktop/gnome/applications/main-menu/main_menu_reordering"
+#define SLAB_UPGRADE_PACKAGE_KEY        "/desktop/gnome/applications/main-menu/upgrade_package_command"
+#define SLAB_UNINSTALL_PACKAGE_KEY      "/desktop/gnome/applications/main-menu/uninstall_package_command"
+#define SLAB_USER_SPECIFIED_APPS_KEY    "/desktop/gnome/applications/main-menu/file-area/user_specified_apps"
+#define SLAB_APPLICATION_USE_DB_KEY     "/desktop/gnome/applications/main-menu/file-area/app_use_db"
+#define SLAB_FILE_ITEM_LIMIT            "/desktop/gnome/applications/main-menu/file-area/item_limit"
+#define SLAB_FILE_BLACKLIST             "/desktop/gnome/applications/main-menu/file-area/file_blacklist"
+#define SLAB_FILE_MANAGER_OPEN_CMD      "/desktop/gnome/applications/main-menu/file-area/file_mgr_open_cmd"
+#define SLAB_FILE_SEND_TO_CMD           "/desktop/gnome/applications/main-menu/file-area/file_send_to_cmd"
+
+gboolean get_slab_gconf_bool (const gchar * key);
+gint get_slab_gconf_int (const gchar * key);
+GSList *get_slab_gconf_slist (const gchar * key);
+void free_slab_gconf_slist_of_strings (GSList * list);
+void free_list_of_strings (GList * list);
+gchar *get_slab_gconf_string (const gchar * key);
+
+GnomeDesktopItem *load_desktop_item_from_gconf_key (const gchar * key);
+GnomeDesktopItem *load_desktop_item_from_unknown (const gchar * id);
+
+gchar *get_package_name_from_desktop_item (GnomeDesktopItem * desktop_item);
+
+gboolean open_desktop_item_exec (GnomeDesktopItem * desktop_item);
+gboolean open_desktop_item_help (GnomeDesktopItem * desktop_item);
+
+gboolean desktop_item_is_in_main_menu (GnomeDesktopItem * desktop_item);
+gboolean desktop_uri_is_in_main_menu (const gchar * uri);
+
+gint desktop_item_location_compare (gconstpointer a, gconstpointer b);
+
+gboolean slab_load_image (GtkImage * image, GtkIconSize size, const gchar * image_id);
+
+gchar *string_replace_once (const gchar * str_template, const gchar * key, const gchar * value);
+
+void spawn_process (const gchar * command);
+void copy_file (const gchar * src_uri, const gchar * dst_uri);
+
+G_END_DECLS
+#endif /* __SLAB_GNOME_UTIL_H__ */
diff --git a/libslab/slab-section.c b/libslab/slab-section.c
new file mode 100644
index 0000000..e416c5f
--- /dev/null
+++ b/libslab/slab-section.c
@@ -0,0 +1,197 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "slab-section.h"
+
+#include <gtk/gtklabel.h>
+#include <gtk/gtkalignment.h>
+
+G_DEFINE_TYPE (SlabSection, slab_section, GTK_TYPE_VBOX)
+
+static void slab_section_finalize (GObject *);
+
+static void slab_section_class_init (SlabSectionClass * slab_section_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (slab_section_class);
+
+	g_obj_class->finalize = slab_section_finalize;
+}
+
+static void
+slab_section_init (SlabSection * section)
+{
+	section->title = NULL;
+	section->contents = NULL;
+}
+
+static void
+slab_section_finalize (GObject * obj)
+{
+	g_assert (IS_SLAB_SECTION (obj));
+	(*G_OBJECT_CLASS (slab_section_parent_class)->finalize) (obj);
+}
+
+static void
+slab_section_set_title_color (GtkWidget * widget)
+{
+	switch (SLAB_SECTION (widget)->style)
+	{
+	case Style1:
+		gtk_widget_modify_fg (SLAB_SECTION (widget)->title, GTK_STATE_NORMAL,
+			&widget->style->bg[GTK_STATE_SELECTED]);
+		break;
+	case Style2:
+		if (SLAB_SECTION (widget)->selected)
+			gtk_widget_modify_fg (SLAB_SECTION (widget)->title, GTK_STATE_NORMAL,
+				&widget->style->dark[GTK_STATE_SELECTED]);
+		else
+			gtk_widget_modify_fg (SLAB_SECTION (widget)->title, GTK_STATE_NORMAL,
+				&widget->style->text[GTK_STATE_INSENSITIVE]);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
+slab_section_style_set (GtkWidget * widget, GtkStyle * prev_style, gpointer user_data)
+{
+	static gboolean recursively_entered = FALSE;
+	if (!recursively_entered)
+	{
+		recursively_entered = TRUE;
+
+		slab_section_set_title_color (widget);
+
+		recursively_entered = FALSE;
+	}
+}
+
+/*
+gboolean
+slab_section_expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer data)
+{
+	gdk_draw_rectangle (widget->window, widget->style->light_gc[GTK_STATE_SELECTED], TRUE,
+		widget->allocation.x, widget->allocation.y,
+		widget->allocation.width + 40, widget->allocation.height);
+
+	return FALSE;
+}
+*/
+
+void
+slab_section_set_selected (SlabSection * section, gboolean selected)
+{
+	if (selected == section->selected)
+		return;
+	section->selected = selected;
+
+	/*
+	   if(selected)
+	   {
+	   section->expose_handler_id = g_signal_connect(G_OBJECT(section),
+	   "expose-event", G_CALLBACK(slab_section_expose_event), NULL);
+	   }
+	   else
+	   {
+	   g_signal_handler_disconnect(section, section->expose_handler_id);
+	   }
+	 */
+
+	slab_section_set_title_color (GTK_WIDGET (section));
+}
+
+GtkWidget *
+slab_section_new_with_markup (const gchar * title_markup, SlabStyle style)
+{
+	SlabSection *section;
+	GtkWidget *align;
+	gchar * widget_theming_name;
+
+	section = g_object_new (SLAB_SECTION_TYPE, NULL);
+	gtk_box_set_homogeneous (GTK_BOX (section), FALSE);
+	gtk_box_set_spacing (GTK_BOX (section), 0);
+	section->style = style;
+	section->selected = FALSE;
+
+	align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+	switch (style)
+	{
+	case Style1:
+		gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 0, 0);
+		widget_theming_name = "slab_section_style1";
+		break;
+	case Style2:
+		gtk_alignment_set_padding (GTK_ALIGNMENT (align), SLAB_TOP_PADDING,
+			SLAB_BOTTOM_PADDING, SLAB_LEFT_PADDING, 0);
+		widget_theming_name = "slab_section_style2";
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	gtk_box_pack_start (GTK_BOX (section), align, TRUE, TRUE, 0);
+
+	section->childbox = GTK_BOX (gtk_vbox_new (FALSE, 10));
+	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (section->childbox));
+
+	section->title = gtk_label_new (title_markup);
+	gtk_label_set_use_markup (GTK_LABEL (section->title), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (section->title), 0.0, 0.5);
+
+	gtk_widget_set_name (GTK_WIDGET (section), widget_theming_name);
+	g_signal_connect (G_OBJECT (section), "style-set", G_CALLBACK (slab_section_style_set),
+		NULL);
+
+	gtk_box_pack_start (section->childbox, section->title, FALSE, FALSE, 0);
+
+	return GTK_WIDGET (section);
+}
+
+GtkWidget *
+slab_section_new (const gchar * title, SlabStyle style)
+{
+	GtkWidget *section;
+	gchar *markup;
+
+	markup = g_strdup_printf ("<span size=\"large\" weight=\"bold\">%s</span>", title);
+	section = slab_section_new_with_markup (markup, style);
+
+	g_free (markup);
+
+	return section;
+}
+
+void
+slab_section_set_title (SlabSection * section, const gchar * title)
+{
+	gchar *markup = g_strdup_printf ("<span size=\"large\">%s</span>", title);
+
+	gtk_label_set_markup (GTK_LABEL (section->title), markup);
+
+	g_free (markup);
+}
+
+void
+slab_section_set_contents (SlabSection * section, GtkWidget * contents)
+{
+	section->contents = contents;
+
+	gtk_box_pack_start (section->childbox, contents, FALSE, FALSE, 0);
+}
diff --git a/libslab/slab-section.h b/libslab/slab-section.h
new file mode 100644
index 0000000..48ee114
--- /dev/null
+++ b/libslab/slab-section.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SLAB_SECTION_H__
+#define __SLAB_SECTION_H__
+
+#include <glib.h>
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+#define SLAB_SECTION_TYPE (slab_section_get_type ())
+#define SLAB_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SLAB_SECTION_TYPE,    SlabSection))
+#define SLAB_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SLAB_SECTION_TYPE, SlabSectionClass))
+#define IS_SLAB_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SLAB_SECTION_TYPE ))
+#define IS_SLAB_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SLAB_SECTION_TYPE ))
+#define SLAB_SECTION_GET_CLASS(obj) (G_TYPE_CHECK_GET_CLASS ((obj), SLAB_SECTION_TYPE, SlabSectionClass))
+
+#define SLAB_TOP_PADDING  5
+#define SLAB_BOTTOM_PADDING  5
+#define SLAB_LEFT_PADDING 10
+
+typedef enum
+{
+	Style1,		/* SlabSections in left pane - no padding */
+	Style2		/* SlabSections in right pane - padding, label text changes as group is selected */
+} SlabStyle;
+
+typedef struct
+{
+	GtkVBox parent_vbox;
+
+	GtkWidget *title;
+	GtkWidget *contents;
+	SlabStyle style;
+	gulong expose_handler_id;
+	GtkBox *childbox;
+	gboolean selected;
+} SlabSection;
+
+typedef struct
+{
+	GtkVBoxClass parent_class;
+} SlabSectionClass;
+
+GType slab_section_get_type (void);
+GtkWidget *slab_section_new (const gchar * title, SlabStyle style);
+GtkWidget *slab_section_new_with_markup (const gchar * title_markup, SlabStyle style);
+void slab_section_set_title (SlabSection * section, const gchar * title);
+void slab_section_set_contents (SlabSection * section, GtkWidget * contents);
+void slab_section_set_selected (SlabSection * section, gboolean selected);
+
+G_END_DECLS
+#endif /* __SLAB_SECTION_H__ */
diff --git a/libslab/system-tile.c b/libslab/system-tile.c
new file mode 100644
index 0000000..75368b5
--- /dev/null
+++ b/libslab/system-tile.c
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2006, 2007 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "system-tile.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <glib/gmacros.h>
+#include <gconf/gconf-client.h>
+#include <libgnomeui/gnome-client.h>
+
+#include "bookmark-agent.h"
+#include "slab-gnome-util.h"
+#include "libslab-utils.h"
+
+G_DEFINE_TYPE (SystemTile, system_tile, NAMEPLATE_TILE_TYPE)
+
+static void system_tile_finalize (GObject *);
+static void system_tile_style_set (GtkWidget *, GtkStyle *);
+
+static void load_image (SystemTile *);
+static GtkWidget *create_header (const gchar *);
+
+static void open_trigger   (Tile *, TileEvent *, TileAction *);
+static void remove_trigger (Tile *, TileEvent *, TileAction *);
+
+static void update_user_list_menu_item (SystemTile *);
+static void agent_notify_cb (GObject *, GParamSpec *, gpointer);
+
+typedef struct {
+	GnomeDesktopItem *desktop_item;
+
+	BookmarkAgent       *agent;
+	BookmarkStoreStatus  agent_status;
+	gulong               notify_signal_id;
+	
+	gchar    *image_id;
+	gboolean  image_is_broken;
+} SystemTilePrivate;
+
+#define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYSTEM_TILE_TYPE, SystemTilePrivate))
+
+GtkWidget *
+system_tile_new (const gchar *desktop_item_id, const gchar *title)
+{
+	SystemTile        *this;
+	SystemTilePrivate *priv;
+
+	gchar     *uri    = NULL;
+	GtkWidget *header = NULL;
+
+	GtkMenu *context_menu;
+
+	TileAction  **actions;
+	TileAction   *action;
+	GtkWidget    *menu_item;
+	GtkContainer *menu_ctnr;
+
+	GnomeDesktopItem *desktop_item = NULL;
+	gchar            *image_id     = NULL;
+	gchar            *header_txt   = NULL;
+
+	gchar *markup;
+
+	AtkObject *accessible = NULL;
+
+
+	desktop_item = libslab_gnome_desktop_item_new_from_unknown_id (desktop_item_id);
+
+	if (desktop_item) {
+		image_id = g_strdup (gnome_desktop_item_get_localestring (desktop_item, "Icon"));
+		uri      = g_strdup (gnome_desktop_item_get_location (desktop_item));
+
+		if (title)
+			header_txt = g_strdup (title);
+		else
+			header_txt = g_strdup (
+				gnome_desktop_item_get_localestring (desktop_item, "Name"));
+	}
+
+	if (! uri)
+		return NULL;
+
+	header = create_header (header_txt);
+
+	context_menu = GTK_MENU (gtk_menu_new ());
+
+	this = g_object_new (
+		SYSTEM_TILE_TYPE,
+		"tile-uri",            uri,
+		"context-menu",        context_menu,
+		"nameplate-image",     gtk_image_new (),
+		"nameplate-header",    header,
+		"nameplate-subheader", NULL,
+		NULL);
+	priv = PRIVATE (this);
+
+	priv->agent = bookmark_agent_get_instance (BOOKMARK_STORE_SYSTEM);
+	g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL);
+
+	priv->notify_signal_id = g_signal_connect (
+		G_OBJECT (priv->agent), "notify", G_CALLBACK (agent_notify_cb), this);
+
+	actions = g_new0 (TileAction *, 2);
+
+	TILE (this)->actions   = actions;
+	TILE (this)->n_actions = 2;
+
+	menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
+
+	markup = g_markup_printf_escaped (_("<b>Open %s</b>"), header_txt);
+	action = tile_action_new (TILE (this), open_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
+	actions [SYSTEM_TILE_ACTION_OPEN] = action;
+	g_free (markup);
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	TILE (this)->default_action = action;
+
+	gtk_container_add (menu_ctnr, gtk_separator_menu_item_new ());
+
+	markup = g_markup_printf_escaped (_("Remove from System Items"));
+	action = tile_action_new (TILE (this), remove_trigger, markup, 0);
+	actions [SYSTEM_TILE_ACTION_REMOVE] = action;
+	g_free (markup);
+
+	menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	gtk_container_add (menu_ctnr, menu_item);
+
+	gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu));
+
+	update_user_list_menu_item (this);
+
+	priv->desktop_item = desktop_item;
+	priv->image_id = g_strdup (image_id);
+
+	load_image (this);
+
+	/* Set up the mnemonic for the tile */
+	gtk_label_set_mnemonic_widget (GTK_LABEL (header), GTK_WIDGET (this));
+
+	/* Set up the accessible name for the tile */
+	accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
+	if (header_txt)
+		atk_object_set_name (accessible, header_txt);
+
+	g_free (header_txt);
+	g_free (image_id);
+	g_free (uri);
+
+	return GTK_WIDGET (this);
+}
+
+static void
+system_tile_class_init (SystemTileClass *this_class)
+{
+	GObjectClass   *g_obj_class  = G_OBJECT_CLASS   (this_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
+
+	g_obj_class->finalize = system_tile_finalize;
+
+	widget_class->style_set = system_tile_style_set;
+
+	g_type_class_add_private (this_class, sizeof (SystemTilePrivate));
+}
+
+static void
+system_tile_init (SystemTile *this)
+{
+	SystemTilePrivate *priv = PRIVATE (this);
+
+	priv->desktop_item    = NULL;
+	priv->image_id        = NULL;
+	priv->image_is_broken = TRUE;
+
+	priv->agent            = NULL;
+	priv->agent_status     = BOOKMARK_STORE_ABSENT;
+	priv->notify_signal_id = 0;
+}
+
+static void
+system_tile_finalize (GObject *g_obj)
+{
+        SystemTilePrivate *priv = PRIVATE (g_obj);
+
+	g_free (priv->image_id);
+	gnome_desktop_item_unref (priv->desktop_item);
+
+	if (priv->notify_signal_id)
+		g_signal_handler_disconnect (priv->agent, priv->notify_signal_id);
+
+	G_OBJECT_CLASS (system_tile_parent_class)->finalize (g_obj);
+}
+
+static void
+system_tile_style_set (GtkWidget *widget, GtkStyle *prev_style)
+{
+	load_image (SYSTEM_TILE (widget));
+}
+
+static void
+load_image (SystemTile *this)
+{
+	SystemTilePrivate *priv = PRIVATE (this);
+
+	GtkImage *image = GTK_IMAGE (NAMEPLATE_TILE (this)->image);
+
+
+	g_object_set (G_OBJECT (image), "icon-size", GTK_ICON_SIZE_MENU, NULL);
+
+	priv->image_is_broken = libslab_gtk_image_set_by_id (image, priv->image_id);
+}
+
+static GtkWidget *
+create_header (const gchar *name)
+{
+	GtkWidget *header;
+
+	header = gtk_label_new (name);
+	gtk_label_set_use_underline (GTK_LABEL (header), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
+
+	return header;
+}
+
+static void
+open_trigger (Tile *this, TileEvent *event, TileAction *action)
+{
+	open_desktop_item_exec (PRIVATE (this)->desktop_item);
+}
+
+static void
+remove_trigger (Tile *this, TileEvent *event, TileAction *action)
+{
+	bookmark_agent_remove_item (PRIVATE (this)->agent, this->uri);
+}
+
+static void
+update_user_list_menu_item (SystemTile *this)
+{
+	SystemTilePrivate *priv = PRIVATE (this);
+
+	TileAction *action;
+	GtkWidget  *item;
+
+
+	action = TILE (this)->actions [SYSTEM_TILE_ACTION_REMOVE];
+
+	if (! action)
+		return;
+
+	item = GTK_WIDGET (tile_action_get_menu_item (action));
+
+	if (! GTK_IS_MENU_ITEM (item))
+		return;
+
+	g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL);
+
+	gtk_widget_set_sensitive (item, (priv->agent_status != BOOKMARK_STORE_DEFAULT_ONLY));
+}
+
+static void
+agent_notify_cb (GObject *g_obj, GParamSpec *pspec, gpointer user_data)
+{
+	update_user_list_menu_item (SYSTEM_TILE (user_data));
+}
diff --git a/libslab/system-tile.h b/libslab/system-tile.h
new file mode 100644
index 0000000..270a7a1
--- /dev/null
+++ b/libslab/system-tile.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the Main Menu.
+ *
+ * Copyright (c) 2006, 2007 Novell, Inc.
+ *
+ * The Main Menu 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.
+ *
+ * The Main Menu 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
+ * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SYSTEM_TILE_H__
+#define __SYSTEM_TILE_H__
+
+#include "nameplate-tile.h"
+
+#include <libgnome/gnome-desktop-item.h>
+
+G_BEGIN_DECLS
+
+#define SYSTEM_TILE_TYPE         (system_tile_get_type ())
+#define SYSTEM_TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), SYSTEM_TILE_TYPE, SystemTile))
+#define SYSTEM_TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), SYSTEM_TILE_TYPE, SystemTileClass))
+#define IS_SYSTEM_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), SYSTEM_TILE_TYPE))
+#define IS_SYSTEM_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), SYSTEM_TILE_TYPE))
+#define SYSTEM_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SYSTEM_TILE_TYPE, SystemTileClass))
+
+typedef struct {
+	NameplateTile nameplate_tile;
+} SystemTile;
+
+typedef struct {
+	NameplateTileClass nameplate_tile_class;
+} SystemTileClass;
+
+#define SYSTEM_TILE_ACTION_OPEN   0
+#define SYSTEM_TILE_ACTION_REMOVE 1
+
+GType system_tile_get_type (void);
+
+GtkWidget *system_tile_new (const gchar *desktop_item_id, const gchar *title);
+
+G_END_DECLS
+#endif
diff --git a/libslab/themed-icon.c b/libslab/themed-icon.c
new file mode 100644
index 0000000..8f87068
--- /dev/null
+++ b/libslab/themed-icon.c
@@ -0,0 +1,165 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "themed-icon.h"
+
+#include "gnome-utils.h"
+
+static void themed_icon_class_init (ThemedIconClass *);
+static void themed_icon_init (ThemedIcon *);
+static void themed_icon_finalize (GObject *);
+static void themed_icon_get_property (GObject *, guint, GValue *, GParamSpec *);
+static void themed_icon_set_property (GObject *, guint, const GValue *, GParamSpec *);
+
+static void themed_icon_show (GtkWidget *);
+static void themed_icon_style_set (GtkWidget *, GtkStyle *);
+
+enum
+{
+	PROP_0,
+	PROP_ICON_ID,
+	PROP_ICON_SIZE
+};
+
+typedef struct
+{
+	gboolean icon_loaded;
+} ThemedIconPrivate;
+
+#define THEMED_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), THEMED_ICON_TYPE, ThemedIconPrivate))
+
+G_DEFINE_TYPE (ThemedIcon, themed_icon, GTK_TYPE_IMAGE)
+
+static void themed_icon_class_init (ThemedIconClass * themed_icon_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (themed_icon_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (themed_icon_class);
+
+	g_obj_class->get_property = themed_icon_get_property;
+	g_obj_class->set_property = themed_icon_set_property;
+	g_obj_class->finalize = themed_icon_finalize;
+
+	widget_class->show = themed_icon_show;
+	widget_class->style_set = themed_icon_style_set;
+
+	g_type_class_add_private (themed_icon_class, sizeof (ThemedIconPrivate));
+
+	g_object_class_install_property (g_obj_class, PROP_ICON_ID, g_param_spec_string ("icon-id",
+			"icon-id", "the identifier of the icon", NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (g_obj_class, PROP_ICON_SIZE,
+		g_param_spec_enum ("icon-size", "icon-size", "the size of the icon",
+			GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_BUTTON,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+themed_icon_init (ThemedIcon * icon)
+{
+	ThemedIconPrivate *priv = THEMED_ICON_GET_PRIVATE (icon);
+
+	priv->icon_loaded = FALSE;
+}
+
+GtkWidget *
+themed_icon_new (const gchar * id, GtkIconSize size)
+{
+	GtkWidget *icon = GTK_WIDGET (g_object_new (
+		THEMED_ICON_TYPE, "icon-id", id, "icon-size", size, NULL));
+
+	return icon;
+}
+
+static void
+themed_icon_finalize (GObject * object)
+{
+	ThemedIcon *icon = THEMED_ICON (object);
+	if (icon->id)
+		g_free (icon->id);
+	(*G_OBJECT_CLASS (themed_icon_parent_class)->finalize) (object);
+}
+
+static void
+themed_icon_get_property (GObject * g_obj, guint prop_id, GValue * value, GParamSpec * param_spec)
+{
+	ThemedIcon *icon = THEMED_ICON (g_obj);
+
+	switch (prop_id)
+	{
+	case PROP_ICON_ID:
+		g_value_set_string (value, icon->id);
+		break;
+
+	case PROP_ICON_SIZE:
+		g_value_set_enum (value, icon->size);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+themed_icon_set_property (GObject * g_obj, guint prop_id, const GValue * value,
+	GParamSpec * param_spec)
+{
+	ThemedIcon *icon = THEMED_ICON (g_obj);
+
+	switch (prop_id)
+	{
+	case PROP_ICON_ID:
+		icon->id = g_strdup (g_value_get_string (value));
+
+/*			gtk_image_load_by_id (GTK_IMAGE (icon), icon->size, icon->id); */
+
+		break;
+
+	case PROP_ICON_SIZE:
+		icon->size = g_value_get_enum (value);
+
+/*			gtk_image_load_by_id (GTK_IMAGE (icon), icon->size, icon->id); */
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+themed_icon_show (GtkWidget * widget)
+{
+	ThemedIcon *icon = THEMED_ICON (widget);
+	ThemedIconPrivate *priv = THEMED_ICON_GET_PRIVATE (icon);
+
+	if (!priv->icon_loaded)
+		priv->icon_loaded = load_image_by_id (GTK_IMAGE (icon), icon->size, icon->id);
+
+	(*GTK_WIDGET_CLASS (themed_icon_parent_class)->show) (widget);
+}
+
+static void
+themed_icon_style_set (GtkWidget * widget, GtkStyle * prev_style)
+{
+	ThemedIcon *icon = THEMED_ICON (widget);
+
+	load_image_by_id (GTK_IMAGE (icon), icon->size, icon->id);
+}
diff --git a/libslab/themed-icon.h b/libslab/themed-icon.h
new file mode 100644
index 0000000..d46b1c3
--- /dev/null
+++ b/libslab/themed-icon.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of libslab.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libslab is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libslab 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __THEMED_ICON_H__
+#define __THEMED_ICON_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define THEMED_ICON_TYPE            (themed_icon_get_type ())
+#define THEMED_ICON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), THEMED_ICON_TYPE, ThemedIcon))
+#define THEMED_ICON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), THEMED_ICON_TYPE, ThemedIconClass))
+#define IS_THEMED_ICON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THEMED_ICON_TYPE))
+#define IS_THEMED_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THEMED_ICON_TYPE))
+#define THEMED_ICON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), THEMED_ICON_TYPE, ThemedIconClass))
+
+typedef struct
+{
+	GtkImage parent;
+
+	GtkIconSize size;
+	gchar *id;
+} ThemedIcon;
+
+typedef struct
+{
+	GtkImageClass parent_class;
+} ThemedIconClass;
+
+GType themed_icon_get_type (void);
+GtkWidget *themed_icon_new (const gchar * id, GtkIconSize size);
+
+G_END_DECLS
+#endif /* __THEMED_ICON_H__ */
diff --git a/libslab/tile-action.c b/libslab/tile-action.c
new file mode 100644
index 0000000..8ad5ff8
--- /dev/null
+++ b/libslab/tile-action.c
@@ -0,0 +1,108 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "tile.h"
+
+G_DEFINE_TYPE (TileAction, tile_action, G_TYPE_OBJECT)
+
+static void tile_action_finalize (GObject *);
+static void tile_action_menu_item_activate_cb (GtkMenuItem *, gpointer);
+
+static void tile_action_class_init (TileActionClass * this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+
+	g_obj_class->finalize = tile_action_finalize;
+}
+
+static void
+tile_action_init (TileAction * this)
+{
+	this->tile = NULL;
+	this->func = 0;
+	this->menu_item = NULL;
+	this->flags = 0;
+}
+
+static void
+tile_action_finalize (GObject * g_object)
+{
+	TileAction *action = TILE_ACTION (g_object);
+	if (action->menu_item)
+		gtk_widget_destroy (GTK_WIDGET (action->menu_item));
+
+	(*G_OBJECT_CLASS (tile_action_parent_class)->finalize) (g_object);
+}
+
+TileAction *
+tile_action_new (Tile * tile, TileActionFunc func, const gchar * menu_item_markup, guint32 flags)
+{
+	TileAction *this = g_object_new (TILE_ACTION_TYPE, NULL);
+
+	this->tile = tile;
+	this->func = func;
+
+	if (menu_item_markup)
+		tile_action_set_menu_item_label (this, menu_item_markup);
+	else
+		this->menu_item = NULL;
+
+	this->flags = flags;
+
+	return this;
+}
+
+void
+tile_action_set_menu_item_label (TileAction * this, const gchar * markup)
+{
+	GtkWidget *label;
+
+	if (this->menu_item)
+	{
+		label = gtk_bin_get_child (GTK_BIN (this->menu_item));
+		gtk_label_set_markup (GTK_LABEL (label), markup);
+	}
+	else
+	{
+		label = gtk_label_new (markup);
+		gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+		gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+		this->menu_item = GTK_MENU_ITEM (gtk_menu_item_new ());
+		gtk_container_add (GTK_CONTAINER (this->menu_item), label);
+
+		g_signal_connect (G_OBJECT (this->menu_item), "activate",
+			G_CALLBACK (tile_action_menu_item_activate_cb), this);
+	}
+}
+
+GtkMenuItem *
+tile_action_get_menu_item (TileAction * this)
+{
+	return this->menu_item;
+}
+
+static void
+tile_action_menu_item_activate_cb (GtkMenuItem * menu_item, gpointer user_data)
+{
+	TileAction *this = TILE_ACTION (user_data);
+
+	tile_trigger_action (this->tile, this);
+}
diff --git a/libslab/tile.c b/libslab/tile.c
new file mode 100644
index 0000000..870944d
--- /dev/null
+++ b/libslab/tile.c
@@ -0,0 +1,621 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "tile.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include "double-click-detector.h"
+
+G_DEFINE_TYPE (Tile, tile, GTK_TYPE_BUTTON)
+
+typedef struct
+{
+	DoubleClickDetector *double_click_detector;
+	
+	gboolean is_dragging;
+} TilePrivate;
+
+static void tile_finalize (GObject *);
+static void tile_get_property (GObject *, guint, GValue *, GParamSpec *);
+static void tile_set_property (GObject *, guint, const GValue *, GParamSpec *);
+static GObject *tile_constructor (GType, guint, GObjectConstructParam *);
+
+static void tile_setup (Tile *);
+
+static void tile_enter (GtkButton * widget);
+static void tile_leave (GtkButton * widget);
+static void tile_clicked (GtkButton *widget);
+
+static gboolean tile_focus_in (GtkWidget *, GdkEventFocus *);
+static gboolean tile_focus_out (GtkWidget *, GdkEventFocus *);
+static gboolean tile_expose (GtkWidget *, GdkEventExpose *);
+static gboolean tile_button_release (GtkWidget *, GdkEventButton *);
+static gboolean tile_key_release (GtkWidget *, GdkEventKey *);
+static gboolean tile_popup_menu (GtkWidget *);
+
+static void tile_popup_menu_position (GtkMenu *, gint *, gint *, gboolean *, gpointer);
+
+static void tile_drag_begin (GtkWidget *, GdkDragContext *);
+static void tile_drag_data_get (GtkWidget *, GdkDragContext *, GtkSelectionData *, guint,
+guint);
+
+static void tile_emit_resource_event (Tile *, TileEventType, guint32);
+
+static void tile_tile_action_triggered (Tile *, TileEvent *, TileAction *);
+static void tile_action_triggered_event_marshal (GClosure *, GValue *, guint, const GValue *,
+gpointer, gpointer);
+
+typedef void (*marshal_func_VOID__POINTER_POINTER) (gpointer, gpointer, gpointer, gpointer);
+
+#define TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TILE_TYPE, TilePrivate))
+
+enum
+{
+	TILE_ACTIVATED_SIGNAL,
+	TILE_IMPLICIT_ENABLE_SIGNAL,
+	TILE_IMPLICIT_DISABLE_SIGNAL,
+	TILE_ACTION_TRIGGERED_SIGNAL,
+	LAST_SIGNAL
+};
+
+static guint tile_signals[LAST_SIGNAL] = { 0 };
+
+enum
+{
+	PROP_0,
+	PROP_TILE_URI,
+	PROP_TILE_CONTEXT_MENU,
+	PROP_TILE_ACTIONS
+};
+
+static void
+tile_class_init (TileClass * this_class)
+{
+	GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
+	GtkButtonClass *button_class = GTK_BUTTON_CLASS (this_class);
+
+	g_obj_class->constructor = tile_constructor;
+	g_obj_class->get_property = tile_get_property;
+	g_obj_class->set_property = tile_set_property;
+	g_obj_class->finalize = tile_finalize;
+
+	widget_class->focus_in_event = tile_focus_in;
+	widget_class->focus_out_event = tile_focus_out;
+	widget_class->expose_event = tile_expose;
+	widget_class->button_release_event = tile_button_release;
+	widget_class->key_release_event = tile_key_release;
+	widget_class->drag_begin = tile_drag_begin;
+	widget_class->drag_data_get = tile_drag_data_get;
+	widget_class->popup_menu = tile_popup_menu;
+
+	button_class->enter = tile_enter;
+	button_class->leave = tile_leave;
+	button_class->clicked = tile_clicked;
+
+	this_class->tile_explicit_enable = NULL;
+	this_class->tile_explicit_disable = NULL;
+	this_class->tile_activated = NULL;
+	this_class->tile_implicit_enable = NULL;
+	this_class->tile_implicit_disable = NULL;
+	this_class->tile_action_triggered = tile_tile_action_triggered;
+
+	g_type_class_add_private (this_class, sizeof (TilePrivate));
+
+	g_object_class_install_property (g_obj_class, PROP_TILE_URI,
+		g_param_spec_string ("tile-uri", "tile-uri", "the uri of the tile", NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (g_obj_class, PROP_TILE_CONTEXT_MENU,
+		g_param_spec_object ("context-menu", "context-menu",
+			"the context menu for the tile", GTK_TYPE_MENU, G_PARAM_READWRITE));
+
+	tile_signals[TILE_ACTIVATED_SIGNAL] = g_signal_new ("tile-activated",
+		G_TYPE_FROM_CLASS (this_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (TileClass, tile_activated),
+		NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	tile_signals[TILE_IMPLICIT_ENABLE_SIGNAL] = g_signal_new ("tile-implicit-enable",
+		G_TYPE_FROM_CLASS (this_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (TileClass, tile_implicit_enable),
+		NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	tile_signals[TILE_IMPLICIT_DISABLE_SIGNAL] = g_signal_new ("tile-implicit-disable",
+		G_TYPE_FROM_CLASS (this_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (TileClass, tile_implicit_disable),
+		NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	tile_signals[TILE_ACTION_TRIGGERED_SIGNAL] = g_signal_new ("tile-action-triggered",
+		G_TYPE_FROM_CLASS (this_class),
+		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (TileClass, tile_action_triggered),
+		NULL, NULL, tile_action_triggered_event_marshal, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
+static GObject *
+tile_constructor (GType type, guint n_param, GObjectConstructParam * param)
+{
+	GObject *g_obj;
+	TilePrivate *priv;
+
+	g_obj = (*G_OBJECT_CLASS (tile_parent_class)->constructor) (type, n_param, param);
+
+	priv = TILE_GET_PRIVATE (g_obj);
+	priv->double_click_detector = double_click_detector_new ();
+
+	tile_setup (TILE (g_obj));
+
+	return g_obj;
+}
+
+static void
+tile_init (Tile * tile)
+{
+	TilePrivate *priv = TILE_GET_PRIVATE (tile);
+
+	tile->uri = NULL;
+	tile->context_menu = NULL;
+	tile->entered = FALSE;
+	tile->enabled = TRUE;
+
+	tile->actions = NULL;
+	tile->n_actions = 0;
+
+	tile->default_action = NULL;
+
+	priv->double_click_detector = NULL;
+	priv->is_dragging = FALSE;
+}
+
+static void
+tile_finalize (GObject * g_object)
+{
+	Tile *tile = TILE (g_object);
+	TilePrivate *priv = TILE_GET_PRIVATE (g_object);
+
+	if (tile->n_actions)	/* this will also free "default_action" entry */
+	{
+		gint x;
+		for (x = 0; x < tile->n_actions; x++)
+		{
+			if (tile->actions[x])
+				g_object_unref (tile->actions[x]);
+		}
+		g_free (tile->actions);
+	}
+
+	if (tile->uri)
+		g_free (tile->uri);
+	if (tile->context_menu)
+		gtk_widget_destroy (GTK_WIDGET (tile->context_menu));
+
+	g_object_unref (priv->double_click_detector);
+
+	(*G_OBJECT_CLASS (tile_parent_class)->finalize) (g_object);
+}
+
+static void
+tile_get_property (GObject * g_obj, guint prop_id, GValue * value, GParamSpec * param_spec)
+{
+	if (!IS_TILE (g_obj))
+		return;
+
+	switch (prop_id)
+	{
+	case PROP_TILE_URI:
+		g_value_set_string (value, TILE (g_obj)->uri);
+		break;
+
+	case PROP_TILE_CONTEXT_MENU:
+		g_value_set_object (value, TILE (g_obj)->context_menu);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+tile_set_property (GObject * g_obj, guint prop_id, const GValue * value, GParamSpec * param_spec)
+{
+	Tile *tile;
+	GtkMenu *menu;
+
+	if (!IS_TILE (g_obj))
+		return;
+
+	tile = TILE (g_obj);
+
+	switch (prop_id)
+	{
+	case PROP_TILE_URI:
+		tile->uri = g_strdup (g_value_get_string (value));
+		break;
+
+	case PROP_TILE_CONTEXT_MENU:
+		menu = g_value_get_object (value);
+
+		if (menu == tile->context_menu)
+			break;
+
+		if (tile->context_menu)
+			gtk_menu_detach (tile->context_menu);
+
+		tile->context_menu = menu;
+
+		if (tile->context_menu)
+			gtk_menu_attach_to_widget (tile->context_menu, GTK_WIDGET (tile), NULL);
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+tile_setup (Tile * tile)
+{
+	gtk_button_set_relief (GTK_BUTTON (tile), GTK_RELIEF_NONE);
+
+	if (tile->uri)
+	{
+		gtk_drag_source_set (GTK_WIDGET (tile), GDK_BUTTON1_MASK, NULL, 0,
+			GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+		gtk_drag_source_add_uri_targets (GTK_WIDGET (tile));
+	}
+}
+
+static void
+tile_enter (GtkButton * widget)
+{
+	gtk_widget_set_state (GTK_WIDGET (widget), TILE_STATE_ENTERED);
+
+	TILE (widget)->entered = TRUE;
+}
+
+static void
+tile_leave (GtkButton * widget)
+{
+	if (GTK_WIDGET_HAS_FOCUS (widget))
+		gtk_widget_set_state (GTK_WIDGET (widget), TILE_STATE_FOCUSED);
+	else
+		gtk_widget_set_state (GTK_WIDGET (widget), GTK_STATE_NORMAL);
+
+	TILE (widget)->entered = FALSE;
+}
+
+static void
+tile_clicked (GtkButton * widget)
+{
+	TileEvent *tile_event;
+
+	tile_event = g_new0 (TileEvent, 1);
+	tile_event->type = TILE_EVENT_ACTIVATED_DOUBLE_CLICK;
+	tile_event->time = gtk_get_current_event_time ();
+
+	g_signal_emit (widget, tile_signals[TILE_ACTIVATED_SIGNAL], 0, tile_event);
+
+	gtk_button_released (widget);
+	g_free (tile_event);
+}
+
+static gboolean
+tile_focus_in (GtkWidget * widget, GdkEventFocus * event)
+{
+	gtk_widget_set_state (widget, TILE_STATE_FOCUSED);
+
+	return FALSE;
+}
+
+static gboolean
+tile_focus_out (GtkWidget * widget, GdkEventFocus * event)
+{
+	if (TILE (widget)->entered)
+		gtk_widget_set_state (widget, TILE_STATE_ENTERED);
+	else
+		gtk_widget_set_state (widget, GTK_STATE_NORMAL);
+
+	return FALSE;
+}
+
+static gboolean
+tile_expose (GtkWidget * widget, GdkEventExpose * event)
+{
+	/* FIXME: there ought to be a better way to prevent the focus from being rendered. */
+
+	gboolean has_focus;
+	gboolean retval;
+
+	if ((has_focus = GTK_WIDGET_HAS_FOCUS (widget)))
+		GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	retval = (*GTK_WIDGET_CLASS (tile_parent_class)->expose_event) (widget, event);
+
+	if (has_focus)
+		GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	return retval;
+}
+
+static gboolean
+tile_button_release (GtkWidget * widget, GdkEventButton * event)
+{
+	Tile *tile = TILE (widget);
+	TilePrivate *priv = TILE_GET_PRIVATE (tile);
+
+	TileEvent *tile_event;
+
+	if (priv->is_dragging)
+	{
+		priv->is_dragging = FALSE;
+
+		return TRUE;
+	}
+
+	switch (event->button)
+	{
+	case 1:
+		tile_event = g_new0 (TileEvent, 1);
+		tile_event->time = event->time;
+
+		if (double_click_detector_is_double_click (priv->double_click_detector, event->time,
+				TRUE))
+			tile_event->type = TILE_EVENT_ACTIVATED_DOUBLE_CLICK;
+		else
+			tile_event->type = TILE_EVENT_ACTIVATED_SINGLE_CLICK;
+
+		g_signal_emit (tile, tile_signals[TILE_ACTIVATED_SIGNAL], 0, tile_event);
+
+		gtk_button_released (GTK_BUTTON (widget));
+		g_free (tile_event);
+
+		break;
+
+	case 3:
+		if (GTK_IS_MENU (tile->context_menu))
+			gtk_menu_popup (tile->context_menu, NULL, NULL, NULL, NULL, event->button,
+				event->time);
+
+		break;
+
+	default:
+		break;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+tile_key_release (GtkWidget * widget, GdkEventKey * event)
+{
+	TileEvent *tile_event;
+
+	if (event->keyval == GDK_Return)
+	{
+		tile_event = g_new0 (TileEvent, 1);
+		tile_event->type = TILE_EVENT_ACTIVATED_KEYBOARD;
+		tile_event->time = event->time;
+
+		g_signal_emit (widget, tile_signals[TILE_ACTIVATED_SIGNAL], 0, tile_event);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+tile_popup_menu_position (GtkMenu * menu, gint * x, gint * y, gboolean * push_in, gpointer data)
+{
+	Tile *tile = TILE (data);
+
+	GtkRequisition req;
+	GtkWidget *top;
+
+	if (!GTK_WIDGET_REALIZED (tile))
+		return;
+
+	gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+	top = gtk_widget_get_toplevel (GTK_WIDGET (tile));
+
+	gdk_window_get_origin (top->window, x, y);
+
+	*x += (top->allocation.width / 2) - (req.width / 2);
+	*y += (top->allocation.height / 2) - (req.height / 2);
+
+	*push_in = FALSE;
+}
+
+static gboolean
+tile_popup_menu (GtkWidget * widget)
+{
+	Tile *tile = TILE (widget);
+
+	if (GTK_IS_MENU (tile->context_menu))
+	{
+		gtk_menu_popup (tile->context_menu, NULL, NULL, tile_popup_menu_position, tile, 0,
+			gtk_get_current_event_time ());
+
+		return TRUE;
+	}
+
+	else
+		return FALSE;
+}
+
+static void
+tile_drag_begin (GtkWidget * widget, GdkDragContext * context)
+{
+	TILE_GET_PRIVATE (widget)->is_dragging = TRUE;
+}
+
+static void
+tile_drag_data_get (GtkWidget * widget, GdkDragContext * context, GtkSelectionData * data,
+	guint info, guint time)
+{
+	gchar *uris[2];
+
+	if (TILE (widget)->uri)
+	{
+		uris[0] = TILE (widget)->uri;
+		uris[1] = NULL;
+
+		gtk_selection_data_set_uris (data, uris);
+	}
+}
+
+static void
+tile_tile_action_triggered (Tile * tile, TileEvent * event, TileAction * action)
+{
+	if (action && action->func)
+		(*action->func) (tile, event, action);
+}
+
+gint
+tile_compare (gconstpointer a, gconstpointer b)
+{
+	if (IS_TILE (a) && IS_TILE (b))
+		return strcmp (TILE (a)->uri, TILE (b)->uri);
+
+	return a - b;
+}
+
+void
+tile_explicit_enable (Tile * this)
+{
+	TileClass *this_class = TILE_GET_CLASS (this);
+
+	if (this_class->tile_explicit_enable)
+		(*this_class->tile_explicit_enable) (this);
+}
+
+void
+tile_explicit_disable (Tile * this)
+{
+	TileClass *this_class = TILE_GET_CLASS (this);
+
+	if (this_class->tile_explicit_disable)
+		(*this_class->tile_explicit_disable) (this);
+}
+
+void
+tile_implicit_enable (Tile * tile)
+{
+	tile_implicit_enable_with_time (tile, GDK_CURRENT_TIME);
+}
+
+void
+tile_implicit_disable (Tile * tile)
+{
+	tile_implicit_disable_with_time (tile, GDK_CURRENT_TIME);
+}
+
+void
+tile_implicit_enable_with_time (Tile * tile, guint32 time)
+{
+	tile_emit_resource_event (tile, TILE_EVENT_IMPLICIT_ENABLE, time);
+}
+
+void
+tile_implicit_disable_with_time (Tile * tile, guint32 time)
+{
+	tile_emit_resource_event (tile, TILE_EVENT_IMPLICIT_DISABLE, time);
+}
+
+static void
+tile_emit_resource_event (Tile * tile, TileEventType type, guint32 time)
+{
+	TileEvent *event;
+	guint signal_id;
+
+	event = g_new0 (TileEvent, 1);
+	event->type = type;
+	event->time = time;
+
+	if (type == TILE_EVENT_IMPLICIT_ENABLE)
+	{
+		signal_id = tile_signals[TILE_IMPLICIT_ENABLE_SIGNAL];
+		tile->enabled = TRUE;
+	}
+	else
+	{
+		signal_id = tile_signals[TILE_IMPLICIT_DISABLE_SIGNAL];
+		tile->enabled = FALSE;
+	}
+
+	g_signal_emit (tile, signal_id, 0, event);
+	g_free (event);
+}
+
+void
+tile_trigger_action (Tile * tile, TileAction * action)
+{
+	tile_trigger_action_with_time (tile, action, GDK_CURRENT_TIME);
+}
+
+void
+tile_trigger_action_with_time (Tile * tile, TileAction * action, guint32 time)
+{
+	TileEvent *event = g_new0 (TileEvent, 1);
+
+	event->type = TILE_EVENT_ACTION_TRIGGERED;
+	event->time = time;
+
+	g_signal_emit (tile, tile_signals[TILE_ACTION_TRIGGERED_SIGNAL], 0, event, action);
+	g_free (event);
+}
+
+static void
+tile_action_triggered_event_marshal (GClosure * closure, GValue * retval, guint n_param,
+	const GValue * param, gpointer invocation_hint, gpointer marshal_data)
+{
+	marshal_func_VOID__POINTER_POINTER callback;
+	GCClosure *cc = (GCClosure *) closure;
+	gpointer data_0, data_1;
+
+	g_return_if_fail (n_param == 3);
+
+	if (G_CCLOSURE_SWAP_DATA (closure))
+	{
+		data_0 = closure->data;
+		data_1 = g_value_peek_pointer (param);
+	}
+	else
+	{
+		data_0 = g_value_peek_pointer (param);
+		data_1 = closure->data;
+	}
+
+	if (marshal_data)
+		callback = (marshal_func_VOID__POINTER_POINTER) marshal_data;
+	else
+		callback = (marshal_func_VOID__POINTER_POINTER) cc->callback;
+
+	callback (data_0, g_value_peek_pointer (param + 1), g_value_peek_pointer (param + 2),
+		data_1);
+}
diff --git a/libslab/tile.h b/libslab/tile.h
new file mode 100644
index 0000000..fdb9368
--- /dev/null
+++ b/libslab/tile.h
@@ -0,0 +1,143 @@
+/*
+ * This file is part of libtile.
+ *
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * Libtile is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * Libtile 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 Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libslab; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __TILE_H__
+#define __TILE_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TILE_TYPE         (tile_get_type ())
+#define TILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TILE_TYPE, Tile))
+#define TILE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), TILE_TYPE, TileClass))
+#define IS_TILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TILE_TYPE))
+#define IS_TILE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), TILE_TYPE))
+#define TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TILE_TYPE, TileClass))
+#define TILE_ACTION_TYPE         (tile_action_get_type ())
+#define TILE_ACTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TILE_ACTION_TYPE, TileAction))
+#define TILE_ACTION_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), TILE_ACTION_TYPE, TileActionClass))
+#define IS_TILE_ACTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TILE_ACTION_TYPE))
+#define IS_TILE_ACTION_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), TILE_ACTION_TYPE))
+#define TILE_ACTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TILE_ACTION_TYPE, TileActionClass))
+#define TILE_ACTION_CHECK_FLAG(action,flag) ((TILE_ACTION (action)->flags & (flag)) != 0)
+#define TILE_STATE_ENTERED GTK_STATE_PRELIGHT
+#define TILE_STATE_FOCUSED GTK_STATE_PRELIGHT
+
+typedef struct _Tile Tile;
+typedef struct _TileClass TileClass;
+typedef struct _TileAction TileAction;
+typedef struct _TileActionClass TileActionClass;
+typedef struct _TileEvent TileEvent;
+
+typedef void (*TileActionFunc) (Tile *, TileEvent *, TileAction *);
+
+typedef enum
+{
+	TILE_EVENT_ACTIVATED_SINGLE_CLICK,
+	TILE_EVENT_ACTIVATED_DOUBLE_CLICK,
+	TILE_EVENT_ACTIVATED_KEYBOARD,
+	TILE_EVENT_IMPLICIT_DISABLE,
+	TILE_EVENT_IMPLICIT_ENABLE,
+	TILE_EVENT_ACTION_TRIGGERED
+} TileEventType;
+
+typedef enum
+{
+	TILE_ACTION_OPENS_NEW_WINDOW = 1 << 0,
+	TILE_ACTION_OPENS_HELP = 1 << 1
+} TileActionFlags;
+
+struct _Tile
+{
+	GtkButton gtk_button;
+
+	gchar *uri;
+	GtkMenu *context_menu;
+	gboolean entered;
+	gboolean enabled;
+
+	TileAction **actions;
+	gint n_actions;
+
+	TileAction *default_action;
+};
+
+struct _TileClass
+{
+	GtkButtonClass gtk_button_class;
+
+	void (*tile_explicit_enable) (Tile *);
+	void (*tile_explicit_disable) (Tile *);
+
+	void (*tile_activated) (Tile *, TileEvent *);
+	void (*tile_implicit_enable) (Tile *, TileEvent *);
+	void (*tile_implicit_disable) (Tile *, TileEvent *);
+	void (*tile_action_triggered) (Tile *, TileEvent *, TileAction *);
+};
+
+struct _TileAction
+{
+	GObject parent;
+
+	Tile *tile;
+
+	TileActionFunc func;
+	GtkMenuItem *menu_item;
+
+	guint32 flags;
+};
+
+struct _TileActionClass
+{
+	GObjectClass parent_class;
+};
+
+struct _TileEvent
+{
+	TileEventType type;
+	guint32 time;
+};
+
+GType tile_get_type (void);
+GType tile_action_get_type (void);
+
+gint tile_compare (gconstpointer a, gconstpointer b);
+
+void tile_explicit_enable (Tile * tile);
+void tile_explicit_disable (Tile * tile);
+
+void tile_implicit_enable (Tile * tile);
+void tile_implicit_disable (Tile * tile);
+void tile_implicit_enable_with_time (Tile * tile, guint32 time);
+void tile_implicit_disable_with_time (Tile * tile, guint32 time);
+
+void tile_trigger_action (Tile * tile, TileAction * action);
+void tile_trigger_action_with_time (Tile * tile, TileAction * action, guint32 time);
+
+TileAction *tile_action_new (Tile * tile, TileActionFunc func, const gchar * menu_item_markup,
+	guint32 flags);
+
+void tile_action_set_menu_item_label (TileAction * action, const gchar * markup);
+GtkMenuItem *tile_action_get_menu_item (TileAction * action);
+
+G_END_DECLS
+#endif



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