summaryrefslogtreecommitdiff
path: root/libslab
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 21:51:44 -0300
committerPerberos <[email protected]>2011-12-01 21:51:44 -0300
commit0b0e6bc987da4fd88a7854ebb12bde705e92c428 (patch)
tree47d329edd31c67eaa36b2147780e37e197e901b5 /libslab
downloadmate-control-center-0b0e6bc987da4fd88a7854ebb12bde705e92c428.tar.bz2
mate-control-center-0b0e6bc987da4fd88a7854ebb12bde705e92c428.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'libslab')
-rw-r--r--libslab/Makefile.am100
-rw-r--r--libslab/Makefile.in804
-rw-r--r--libslab/app-resizer.c315
-rw-r--r--libslab/app-resizer.h76
-rw-r--r--libslab/app-shell.c1413
-rw-r--r--libslab/app-shell.h145
-rw-r--r--libslab/application-tile.c886
-rw-r--r--libslab/application-tile.h70
-rw-r--r--libslab/bookmark-agent.c1275
-rw-r--r--libslab/bookmark-agent.h94
-rw-r--r--libslab/directory-tile.c669
-rw-r--r--libslab/directory-tile.h61
-rw-r--r--libslab/document-tile.c1130
-rw-r--r--libslab/document-tile.h70
-rw-r--r--libslab/double-click-detector.c85
-rw-r--r--libslab/double-click-detector.h62
-rw-r--r--libslab/libslab-utils.c716
-rw-r--r--libslab/libslab-utils.h46
-rw-r--r--libslab/mate-utils.c346
-rw-r--r--libslab/mate-utils.h44
-rw-r--r--libslab/nameplate-tile.c290
-rw-r--r--libslab/nameplate-tile.h59
-rw-r--r--libslab/nld-marshal.list1
-rw-r--r--libslab/search-bar.c361
-rw-r--r--libslab/search-bar.h76
-rw-r--r--libslab/search-context-picker.c196
-rw-r--r--libslab/search-context-picker.h62
-rw-r--r--libslab/search-entry-watermark.svg58
-rw-r--r--libslab/search-entry.c150
-rw-r--r--libslab/search-entry.h54
-rw-r--r--libslab/shell-window.c147
-rw-r--r--libslab/shell-window.h69
-rw-r--r--libslab/slab-mate-util.c471
-rw-r--r--libslab/slab-mate-util.h80
-rw-r--r--libslab/slab-section.c194
-rw-r--r--libslab/slab-section.h75
-rw-r--r--libslab/slab.h45
-rw-r--r--libslab/system-tile.c285
-rw-r--r--libslab/system-tile.h57
-rw-r--r--libslab/themed-icon.c165
-rw-r--r--libslab/themed-icon.h57
-rw-r--r--libslab/tile-action.c108
-rw-r--r--libslab/tile.c621
-rw-r--r--libslab/tile.h147
44 files changed, 12235 insertions, 0 deletions
diff --git a/libslab/Makefile.am b/libslab/Makefile.am
new file mode 100644
index 00000000..e9cfb986
--- /dev/null
+++ b/libslab/Makefile.am
@@ -0,0 +1,100 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(LIBSLAB_CFLAGS) \
+ $(WARN_CFLAGS)
+
+
+HEADER_FILES= \
+ $(BUILT_SOURCES) \
+ app-resizer.h \
+ app-shell.h \
+ application-tile.h \
+ bookmark-agent.h \
+ directory-tile.h \
+ document-tile.h \
+ double-click-detector.h \
+ mate-utils.h \
+ libslab-utils.h \
+ nameplate-tile.h \
+ search-bar.h \
+ search-context-picker.h \
+ search-entry.h \
+ shell-window.h \
+ slab-mate-util.h \
+ slab-section.h \
+ slab.h \
+ system-tile.h \
+ tile.h
+
+if !LIBSLAB_FOR_INTERNAL_USE
+lib_LTLIBRARIES = libslab.la
+else
+noinst_LTLIBRARIES = libslab.la
+endif
+
+libslab_la_SOURCES = \
+ $(MARSHAL_GENERATED) \
+ app-resizer.c \
+ app-shell.c \
+ application-tile.c \
+ bookmark-agent.c \
+ directory-tile.c \
+ document-tile.c \
+ double-click-detector.c \
+ mate-utils.c \
+ libslab-utils.c \
+ nameplate-tile.c \
+ search-bar.c \
+ search-context-picker.c \
+ search-entry-watermark.h \
+ search-entry.c \
+ shell-window.c \
+ slab-mate-util.c \
+ slab-section.c \
+ system-tile.c \
+ themed-icon.c \
+ themed-icon.h \
+ tile-action.c \
+ tile.c
+
+if !LIBSLAB_FOR_INTERNAL_USE
+libslab_includedir = $(includedir)/libslab
+libslab_include_HEADERS = $(HEADER_FILES)
+
+libslab_la_LDFLAGS = -version-info $(LT_VERSION)
+endif
+
+libslab_la_LIBADD = $(LIBSLAB_LIBS)
+
+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
+
+-include $(top_srcdir)/git.mk
diff --git a/libslab/Makefile.in b/libslab/Makefile.in
new file mode 100644
index 00000000..882cfafe
--- /dev/null
+++ b/libslab/Makefile.in
@@ -0,0 +1,804 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libslab
+DIST_COMMON = $(am__libslab_include_HEADERS_DIST) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/mate-doc-utils.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libslab_includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libslab_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__objects_1 = nld-marshal.lo
+am_libslab_la_OBJECTS = $(am__objects_1) app-resizer.lo app-shell.lo \
+ application-tile.lo bookmark-agent.lo directory-tile.lo \
+ document-tile.lo double-click-detector.lo mate-utils.lo \
+ libslab-utils.lo nameplate-tile.lo search-bar.lo \
+ search-context-picker.lo search-entry.lo shell-window.lo \
+ slab-mate-util.lo slab-section.lo system-tile.lo \
+ themed-icon.lo tile-action.lo tile.lo
+libslab_la_OBJECTS = $(am_libslab_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libslab_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libslab_la_LDFLAGS) $(LDFLAGS) -o $@
+@LIBSLAB_FOR_INTERNAL_USE_FALSE@am_libslab_la_rpath = -rpath $(libdir)
+@LIBSLAB_FOR_INTERNAL_USE_TRUE@am_libslab_la_rpath =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libslab_la_SOURCES)
+DIST_SOURCES = $(libslab_la_SOURCES)
+am__libslab_include_HEADERS_DIST = search-entry-watermark.h \
+ nld-marshal.c nld-marshal.h app-resizer.h app-shell.h \
+ application-tile.h bookmark-agent.h directory-tile.h \
+ document-tile.h double-click-detector.h mate-utils.h \
+ libslab-utils.h nameplate-tile.h search-bar.h \
+ search-context-picker.h search-entry.h shell-window.h \
+ slab-mate-util.h slab-section.h slab.h system-tile.h tile.h
+HEADERS = $(libslab_include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APP_INDICATOR_CFLAGS = @APP_INDICATOR_CFLAGS@
+APP_INDICATOR_LIBS = @APP_INDICATOR_LIBS@
+AR = @AR@
+AT_CAPPLET_CFLAGS = @AT_CAPPLET_CFLAGS@
+AT_CAPPLET_LIBS = @AT_CAPPLET_LIBS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAPPLET_CFLAGS = @CAPPLET_CFLAGS@
+CAPPLET_LIBS = @CAPPLET_LIBS@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFAULT_APPLICATIONS_CAPPLET_CFLAGS = @DEFAULT_APPLICATIONS_CAPPLET_CFLAGS@
+DEFAULT_APPLICATIONS_CAPPLET_LIBS = @DEFAULT_APPLICATIONS_CAPPLET_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DISPLAY_CAPPLET_CFLAGS = @DISPLAY_CAPPLET_CFLAGS@
+DISPLAY_CAPPLET_LIBS = @DISPLAY_CAPPLET_LIBS@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOC_USER_FORMATS = @DOC_USER_FORMATS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXTERNAL_LIBSLAB_CFLAGS = @EXTERNAL_LIBSLAB_CFLAGS@
+EXTERNAL_LIBSLAB_LIBS = @EXTERNAL_LIBSLAB_LIBS@
+FGREP = @FGREP@
+FONT_CAPPLET_CFLAGS = @FONT_CAPPLET_CFLAGS@
+FONT_CAPPLET_LIBS = @FONT_CAPPLET_LIBS@
+FONT_VIEWER_CFLAGS = @FONT_VIEWER_CFLAGS@
+FONT_VIEWER_LIBS = @FONT_VIEWER_LIBS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GREP = @GREP@
+GSD_DBUS_CFLAGS = @GSD_DBUS_CFLAGS@
+GSD_DBUS_LIBS = @GSD_DBUS_LIBS@
+GTK_ENGINE_DIR = @GTK_ENGINE_DIR@
+HELP_DIR = @HELP_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCANBERRA_GTK_CFLAGS = @LIBCANBERRA_GTK_CFLAGS@
+LIBCANBERRA_GTK_LIBS = @LIBCANBERRA_GTK_LIBS@
+LIBEBOOK_CFLAGS = @LIBEBOOK_CFLAGS@
+LIBEBOOK_LIBS = @LIBEBOOK_LIBS@
+LIBMATEKBDUI_CFLAGS = @LIBMATEKBDUI_CFLAGS@
+LIBMATEKBDUI_LIBS = @LIBMATEKBDUI_LIBS@
+LIBMATEKBD_CFLAGS = @LIBMATEKBD_CFLAGS@
+LIBMATEKBD_LIBS = @LIBMATEKBD_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSLAB_CFLAGS = @LIBSLAB_CFLAGS@
+LIBSLAB_LIBS = @LIBSLAB_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MARCO_CFLAGS = @MARCO_CFLAGS@
+MARCO_LIBS = @MARCO_LIBS@
+MATECC_CAPPLETS_CFLAGS = @MATECC_CAPPLETS_CFLAGS@
+MATECC_CAPPLETS_CLEANFILES = @MATECC_CAPPLETS_CLEANFILES@
+MATECC_CAPPLETS_EXTRA_DIST = @MATECC_CAPPLETS_EXTRA_DIST@
+MATECC_CAPPLETS_LIBS = @MATECC_CAPPLETS_LIBS@
+MATECC_CFLAGS = @MATECC_CFLAGS@
+MATECC_LIBS = @MATECC_LIBS@
+MATECC_SHELL_CFLAGS = @MATECC_SHELL_CFLAGS@
+MATECC_SHELL_LIBS = @MATECC_SHELL_LIBS@
+MATECONFTOOL = @MATECONFTOOL@
+MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@
+MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@
+MATE_DESKTOP_CFLAGS = @MATE_DESKTOP_CFLAGS@
+MATE_DESKTOP_LIBS = @MATE_DESKTOP_LIBS@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OMF_DIR = @OMF_DIR@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SCREENSAVER_LIBS = @SCREENSAVER_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TYPING_BREAK = @TYPING_BREAK@
+TYPING_CFLAGS = @TYPING_CFLAGS@
+TYPING_LIBS = @TYPING_LIBS@
+UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XCURSOR_CFLAGS = @XCURSOR_CFLAGS@
+XCURSOR_LIBS = @XCURSOR_LIBS@
+XF86MISC_LIBS = @XF86MISC_LIBS@
+XGETTEXT = @XGETTEXT@
+XINPUT_CFLAGS = @XINPUT_CFLAGS@
+XINPUT_LIBS = @XINPUT_LIBS@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(LIBSLAB_CFLAGS) \
+ $(WARN_CFLAGS)
+
+HEADER_FILES = \
+ $(BUILT_SOURCES) \
+ app-resizer.h \
+ app-shell.h \
+ application-tile.h \
+ bookmark-agent.h \
+ directory-tile.h \
+ document-tile.h \
+ double-click-detector.h \
+ mate-utils.h \
+ libslab-utils.h \
+ nameplate-tile.h \
+ search-bar.h \
+ search-context-picker.h \
+ search-entry.h \
+ shell-window.h \
+ slab-mate-util.h \
+ slab-section.h \
+ slab.h \
+ system-tile.h \
+ tile.h
+
+@LIBSLAB_FOR_INTERNAL_USE_FALSE@lib_LTLIBRARIES = libslab.la
+@LIBSLAB_FOR_INTERNAL_USE_TRUE@noinst_LTLIBRARIES = libslab.la
+libslab_la_SOURCES = \
+ $(MARSHAL_GENERATED) \
+ app-resizer.c \
+ app-shell.c \
+ application-tile.c \
+ bookmark-agent.c \
+ directory-tile.c \
+ document-tile.c \
+ double-click-detector.c \
+ mate-utils.c \
+ libslab-utils.c \
+ nameplate-tile.c \
+ search-bar.c \
+ search-context-picker.c \
+ search-entry-watermark.h \
+ search-entry.c \
+ shell-window.c \
+ slab-mate-util.c \
+ slab-section.c \
+ system-tile.c \
+ themed-icon.c \
+ themed-icon.h \
+ tile-action.c \
+ tile.c
+
+@LIBSLAB_FOR_INTERNAL_USE_FALSE@libslab_includedir = $(includedir)/libslab
+@LIBSLAB_FOR_INTERNAL_USE_FALSE@libslab_include_HEADERS = $(HEADER_FILES)
+@LIBSLAB_FOR_INTERNAL_USE_FALSE@libslab_la_LDFLAGS = -version-info $(LT_VERSION)
+libslab_la_LIBADD = $(LIBSLAB_LIBS)
+MARSHAL_GENERATED = nld-marshal.c nld-marshal.h
+BUILT_SOURCES = \
+ search-entry-watermark.h \
+ $(MARSHAL_GENERATED)
+
+CLEANFILES = \
+ search-entry-watermark.h \
+ $(MARSHAL_GENERATED)
+
+EXTRA_DIST = \
+ search-entry-watermark.svg \
+ nld-marshal.list
+
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libslab/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libslab/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libslab.la: $(libslab_la_OBJECTS) $(libslab_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libslab_la_LINK) $(am_libslab_la_rpath) $(libslab_la_OBJECTS) $(libslab_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-resizer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-shell.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/application-tile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bookmark-agent.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/directory-tile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/document-tile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/double-click-detector.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libslab-utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate-utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nameplate-tile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nld-marshal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search-bar.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search-context-picker.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search-entry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell-window.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slab-mate-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slab-section.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/system-tile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/themed-icon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tile-action.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tile.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libslab_includeHEADERS: $(libslab_include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libslab_includedir)" || $(MKDIR_P) "$(DESTDIR)$(libslab_includedir)"
+ @list='$(libslab_include_HEADERS)'; test -n "$(libslab_includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libslab_includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libslab_includedir)" || exit $$?; \
+ done
+
+uninstall-libslab_includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libslab_include_HEADERS)'; test -n "$(libslab_includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libslab_includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libslab_includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libslab_includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-libslab_includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libslab_includeHEADERS
+
+.MAKE: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \
+ ctags distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libslab_includeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libslab_includeHEADERS
+
+
+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 '"' >> $@
+
+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 )
+
+-include $(top_srcdir)/git.mk
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libslab/app-resizer.c b/libslab/app-resizer.c
new file mode 100644
index 00000000..8734cd86
--- /dev/null
+++ b/libslab/app-resizer.c
@@ -0,0 +1,315 @@
+/*
+ * 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 <libmate/mate-desktop-item.h>
+
+#include "app-shell.h"
+#include "app-resizer.h"
+
+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);
+
+G_DEFINE_TYPE (AppResizer, app_resizer, GTK_TYPE_LAYOUT);
+
+
+static void
+app_resizer_class_init (AppResizerClass * klass)
+{
+ GtkWidgetClass *widget_class;
+
+ ((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 (app_resizer_parent_class)->size_allocate)
+ (*GTK_WIDGET_CLASS (app_resizer_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 (app_resizer_parent_class)->size_allocate)
+ (*GTK_WIDGET_CLASS (app_resizer_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 (app_resizer_parent_class)->size_allocate)
+ (*GTK_WIDGET_CLASS (app_resizer_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 00000000..af974653
--- /dev/null
+++ b/libslab/app-resizer.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 __APP_RESIZER_H__
+#define __APP_RESIZER_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libslab/app-shell.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __APP_RESIZER_H__ */
diff --git a/libslab/app-shell.c b/libslab/app-shell.c
new file mode 100644
index 00000000..0944f404
--- /dev/null
+++ b/libslab/app-shell.c
@@ -0,0 +1,1413 @@
+/*
+ * 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 <libmate/mate-desktop-item.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-mate-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, MateMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive);
+static void generate_launchers (MateMenuTreeDirectory * 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, MateDesktopItem * 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 matemenu_tree_changed_callback (MateMenuTree * 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_app),
+ &app_data->main_app_window_x, &app_data->main_app_window_y);
+ /* printf("x:%d, y:%d\n", app_data->main_app_window_x, app_data->main_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_app);
+}
+
+void
+show_shell (AppShellData * app_data)
+{
+ gtk_widget_show_all (app_data->main_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_app_window_shown_once)
+ gtk_window_move (GTK_WINDOW (app_data->main_app),
+ app_data->main_app_window_x, app_data->main_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_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 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ app_data->main_app = main_app;
+ gtk_widget_set_name (main_app, app_name);
+ gtk_window_set_title (GTK_WINDOW (main_app), title);
+ /* gtk_window_set_default_size(GTK_WINDOW(main_app), width, height); */
+ gtk_window_set_icon_name (GTK_WINDOW (main_app), window_icon);
+ gtk_container_add (GTK_CONTAINER (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_app), GTK_WIN_POS_CENTER);
+ if (!hidden)
+ show_shell (app_data);
+
+ 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;
+ gchar *str1;
+ gchar *str2;
+
+ 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);
+ }
+
+ str1 = g_markup_printf_escaped ("<b>%s</b>", app_data->filter_string);
+ str2 = g_strdup_printf (_("Your filter \"%s\" does not match any items."), str1);
+ markup = g_strdup_printf ("<span size=\"large\"><b>%s</b></span>\n\n%s",
+ _("No matches found."), str2);
+ 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 (str1);
+ g_free (str2);
+ 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
+matemenu_tree_changed_callback (MateMenuTree * old_tree, gpointer user_data)
+{
+ /*
+ This method only gets called on the first change (matemenu 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 * mateconf_keys_prefix,
+ GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close)
+{
+ AppShellData *app_data = g_new0 (AppShellData, 1);
+ app_data->mateconf_prefix = mateconf_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)
+{
+ MateMenuTreeDirectory *root_dir;
+ GSList *contents, *l;
+ gboolean need_misc = FALSE;
+
+ if (!app_data->tree)
+ {
+ app_data->tree = matemenu_tree_lookup (app_data->menu_name, MATEMENU_TREE_FLAGS_NONE);
+ matemenu_tree_add_monitor (app_data->tree, matemenu_tree_changed_callback, app_data);
+ }
+ root_dir = matemenu_tree_get_root_directory (app_data->tree);
+ if (root_dir)
+ contents = matemenu_tree_directory_get_contents (root_dir);
+ else
+ contents = NULL;
+ 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 MATE/GTK way to do this. */
+ }
+
+ for (l = contents; l; l = l->next)
+ {
+ const char *category;
+ MateMenuTreeItem *item = l->data;
+
+ switch (matemenu_tree_item_get_type (item))
+ {
+ case MATEMENU_TREE_ITEM_DIRECTORY:
+ category = matemenu_tree_directory_get_name ((MateMenuTreeDirectory*)item);
+ generate_category(category, (MateMenuTreeDirectory*)item, app_data, TRUE);
+ break;
+ case MATEMENU_TREE_ITEM_ENTRY:
+ need_misc = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ matemenu_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;
+ }
+
+ matemenu_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, MateMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive)
+{
+ CategoryData *data;
+ /* This is not needed. MateMenu 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 matemenu 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 (MateDesktopItem * item)
+{
+ static const gchar *COMMAND_LINE_LOCKDOWN_MATECONF_KEY =
+ "/desktop/mate/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_mateconf_bool (COMMAND_LINE_LOCKDOWN_MATECONF_KEY);
+ }
+
+ /* This seems like an ugly hack but it's the way it's currently done in the old control center */
+ exec = mate_desktop_item_get_string (item, MATE_DESKTOP_ITEM_EXEC);
+
+ /* discard xscreensaver if mate-screensaver is installed */
+ if ((exec && !strcmp (exec, "xscreensaver-demo"))
+ && (path = g_find_program_in_path ("mate-screensaver-preferences")))
+ {
+ g_free (path);
+ return TRUE;
+ }
+
+ /* discard mate-keyring-manager if CASA is installed */
+ if ((exec && !strcmp (exec, "mate-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 =
+ mate_desktop_item_get_string (item, MATE_DESKTOP_ITEM_CATEGORIES);
+ if (g_strrstr (categories, COMMAND_LINE_LOCKDOWN_DESKTOP_CATEGORY))
+ {
+ /* printf ("eliminating %s\n", mate_desktop_item_get_location (item)); */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+generate_launchers (MateMenuTreeDirectory * root_dir, AppShellData * app_data, CategoryData * cat_data, gboolean recursive)
+{
+ MateDesktopItem *desktop_item;
+ const gchar *desktop_file;
+ GSList *contents, *l;
+
+ contents = matemenu_tree_directory_get_contents (root_dir);
+ for (l = contents; l; l = l->next)
+ {
+ switch (matemenu_tree_item_get_type (l->data))
+ {
+ case MATEMENU_TREE_ITEM_DIRECTORY:
+ /* g_message ("Found sub-category %s", matemenu_tree_directory_get_name (l->data)); */
+ if (recursive)
+ generate_launchers (l->data, app_data, cat_data, TRUE);
+ break;
+ case MATEMENU_TREE_ITEM_ENTRY:
+ /* g_message ("Found item name is:%s", matemenu_tree_entry_get_name (l->data)); */
+ desktop_file = matemenu_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 MateMenuTreeEntry*
+ 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 = mate_desktop_item_new_from_file (desktop_file, 0, NULL);
+ if (!desktop_item)
+ {
+ g_critical ("Failure - mate_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);
+ mate_desktop_item_unref (desktop_item);
+ break;
+ default:
+ break;
+ }
+
+ matemenu_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 *mateconf_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;
+
+ mateconf_key = g_strdup_printf ("%s%s", app_data->mateconf_prefix, NEW_APPS_FILE_KEY);
+ basename = get_slab_mateconf_string (mateconf_key);
+ g_free (mateconf_key);
+ if (!basename)
+ {
+ g_warning ("Failure getting mateconf 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);
+ MateDesktopItem *item =
+ application_tile_get_desktop_item (APPLICATION_TILE (tile));
+ const gchar *uri = mate_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);
+ MateDesktopItem *item =
+ application_tile_get_desktop_item (APPLICATION_TILE (tile));
+ const gchar *uri = mate_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, MateDesktopItem * 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 (mate_desktop_item_get_location (desktop_item),
+ app_data->icon_size, app_data->show_tile_generic_name, app_data->mateconf_prefix);
+ gtk_widget_set_size_request (launcher, SIZING_TILE_WIDTH, -1);
+
+ filepath =
+ g_strdup (mate_desktop_item_get_string (desktop_item, MATE_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 matemenu 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 *mateconf_key;
+
+ tile_trigger_action (launcher, launcher->actions[APPLICATION_TILE_ACTION_START]);
+
+ mateconf_key = g_strdup_printf ("%s%s", app_data->mateconf_prefix, EXIT_SHELL_ON_ACTION_START);
+ if (get_slab_mateconf_bool (mateconf_key))
+ {
+ if (app_data->exit_on_close)
+ gtk_main_quit ();
+ else
+ hide_shell (app_data);
+ }
+ g_free (mateconf_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->mateconf_prefix, EXIT_SHELL_ON_ACTION_START);
+ }
+
+ else if (action == launcher->actions[APPLICATION_TILE_ACTION_HELP])
+ {
+ temp = g_strdup_printf ("%s%s", app_data->mateconf_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->mateconf_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->mateconf_prefix,
+ EXIT_SHELL_ON_ACTION_UPGRADE_UNINSTALL);
+ }
+
+ if (temp)
+ {
+ if (get_slab_mateconf_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 00000000..309965a3
--- /dev/null
+++ b/libslab/app-shell.h
@@ -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
+ */
+
+#ifndef __APP_SHELL_H__
+#define __APP_SHELL_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#define MATEMENU_I_KNOW_THIS_IS_UNSTABLE
+#include <matemenu-tree.h>
+#include <libmate/mate-desktop-item.h>
+
+#include <libslab/slab-section.h>
+#include <libslab/tile.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_app;
+ gint main_app_window_x;
+ gint main_app_window_y;
+ gboolean main_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 *mateconf_prefix;
+ const gchar *menu_name;
+ NewAppConfig *new_apps;
+ MateMenuTree *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;
+ MateDesktopItem *item;
+} AppAction;
+
+typedef struct
+{
+ long time;
+ MateDesktopItem *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 * mateconf_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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __APP_SHELL_H__ */
diff --git a/libslab/application-tile.c b/libslab/application-tile.c
new file mode 100644
index 00000000..81a1e373
--- /dev/null
+++ b/libslab/application-tile.c
@@ -0,0 +1,886 @@
+/*
+ * 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 "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <mateconf/mateconf-client.h>
+#include <unistd.h>
+
+#include "slab-mate-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 (MateDesktopItem *);
+static void update_startup_menu_item (ApplicationTile *);
+
+typedef struct {
+ MateDesktopItem *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_MATECONF_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_MATECONF_PREFIX,
+ g_param_spec_string (
+ "mateconf-prefix", "mateconf-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 *mateconf_prefix)
+{
+ ApplicationTile *this;
+ ApplicationTilePrivate *priv;
+
+ const gchar *uri = NULL;
+
+ MateDesktopItem *desktop_item;
+
+
+ desktop_item = load_desktop_item_from_unknown (desktop_item_id);
+
+ if (
+ desktop_item &&
+ mate_desktop_item_get_entry_type (desktop_item) == MATE_DESKTOP_ITEM_TYPE_APPLICATION
+ )
+ uri = mate_desktop_item_get_location (desktop_item);
+
+ if (! uri) {
+ if (desktop_item)
+ mate_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, mateconf_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->mateconf_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->mateconf_prefix) {
+ g_free (tile->mateconf_prefix);
+ tile->mateconf_prefix = NULL;
+ }
+
+ if (priv->desktop_item) {
+ mate_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_MATECONF_PREFIX:
+ g_value_set_string (value, tile->mateconf_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_MATECONF_PREFIX:
+ if (tile->mateconf_prefix)
+ g_free (tile->mateconf_prefix);
+ tile->mateconf_prefix = g_strdup (g_value_get_string (value));
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+application_tile_setup (ApplicationTile *this, const gchar *mateconf_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;
+ gchar *str;
+
+ /*Fixme - need to address the entire mateconf 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 (mate_desktop_item_get_localestring (priv->desktop_item, "Icon"));
+ image = themed_icon_new (priv->image_id, priv->image_size);
+
+ name = mate_desktop_item_get_localestring (priv->desktop_item, "Name");
+ desc = mate_desktop_item_get_localestring (priv->desktop_item, "GenericName");
+ comment = mate_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,
+ "mateconf-prefix", mateconf_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 */
+
+ str = g_strdup_printf (_("Start %s"), this->name);
+ markup = g_markup_printf_escaped ("<b>%s</b>", str);
+ action = tile_action_new (TILE (this), start_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
+ actions [APPLICATION_TILE_ACTION_START] = action;
+ g_free (markup);
+ g_free (str);
+
+ 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 (mate_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->mateconf_prefix && ! g_str_has_prefix (this->mateconf_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_line_wrap (GTK_LABEL (header), TRUE);
+ 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 *mateconf_key)
+{
+ gchar *cmd;
+ gchar *path;
+ gchar *args;
+
+ gboolean retval;
+
+ cmd = get_slab_mateconf_string (mateconf_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 *mateconf_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_mateconf_string (mateconf_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 (mate_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 = mate_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 (mate_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);
+}
+
+MateDesktopItem *
+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 (MateDesktopItem *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 (mate_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);
+ }
+
+ /* mate-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], "mate", "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 00000000..612e8760
--- /dev/null
+++ b/libslab/application-tile.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <libslab/nameplate-tile.h>
+
+#include <libmate/mate-desktop-item.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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 *mateconf_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 *mateconf_prefix);
+
+MateDesktopItem *application_tile_get_desktop_item (ApplicationTile * tile);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libslab/bookmark-agent.c b/libslab/bookmark-agent.c
new file mode 100644
index 00000000..8e646a72
--- /dev/null
+++ b/libslab/bookmark-agent.c
@@ -0,0 +1,1275 @@
+/*
+ * 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 "mate-main-menu"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include "libslab-utils.h"
+
+#define MODIFIABLE_APPS_MATECONF_KEY "/desktop/mate/applications/main-menu/lock-down/user_modifiable_apps"
+#define MODIFIABLE_DOCS_MATECONF_KEY "/desktop/mate/applications/main-menu/lock-down/user_modifiable_docs"
+#define MODIFIABLE_DIRS_MATECONF_KEY "/desktop/mate/applications/main-menu/lock-down/user_modifiable_dirs"
+#define MODIFIABLE_SYS_MATECONF_KEY "/desktop/mate/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 mateconf_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 mateconf_notify_cb (MateConfClient *, guint, MateConfEntry *, 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_purge_items (BookmarkAgent *this)
+{
+ BookmarkAgentPrivate *priv = PRIVATE (this);
+
+ GError *error = NULL;
+
+ gchar **uris = NULL;
+ gsize uris_len;
+ gint i;
+ g_return_if_fail (priv->user_modifiable);
+
+ uris = g_bookmark_file_get_uris (priv->store, &uris_len);
+ if (TYPE_IS_RECENT (priv->type)) {
+ for (i = 0; i < uris_len; i++) {
+ gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uris [i], & error);
+
+ if (error)
+ libslab_handle_g_error (
+ & error, "%s: unable to remove [%s] from %s.",
+ G_STRFUNC, priv->store_path, uris [i]);
+ }
+ } else {
+ for (i = 0; i < uris_len; i++) {
+ g_bookmark_file_remove_item (priv->store, uris [i], NULL);
+ }
+ save_store (this);
+ }
+ g_strfreev (uris);
+}
+
+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->mateconf_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_MATECONF_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_MATECONF_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_MATECONF_KEY;
+ priv->store_filename = USER_DIRS_STORE_FILE_NAME;
+ priv->create_item = create_dir_item;
+
+ priv->user_modifiable = GPOINTER_TO_INT (libslab_get_mateconf_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_MATECONF_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_mateconf_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->mateconf_monitor = libslab_mateconf_notify_add (
+ priv->lockdown_key, mateconf_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_mateconf_notify_remove (priv->mateconf_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);
+
+ MateDesktopItem *ditem;
+ gchar *uri_new = NULL;
+
+ ditem = libslab_mate_desktop_item_new_from_unknown_id (uri);
+
+ if (ditem) {
+ uri_new = g_strdup (mate_desktop_item_get_location (ditem));
+ mate_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_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
+ if (! dir)
+ 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 = g_strdup (C_("Home folder", "Home"));
+ icon = "user-home";
+ }
+ else if (! strcmp (uri, "DOCUMENTS")) {
+ path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
+ if (! path)
+ 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_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
+ if (! path)
+ path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
+ name = _("Desktop");
+ uri_new = g_filename_to_uri (path, NULL, NULL);
+ icon = "user-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-caja-search")) {
+ icon = "system-search";
+
+ path = g_build_filename (g_get_home_dir (), ".caja", "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
+mateconf_notify_cb (MateConfClient *client, guint conn_id,
+ MateConfEntry *entry, gpointer user_data)
+{
+ BookmarkAgent *this = BOOKMARK_AGENT (user_data);
+ BookmarkAgentPrivate *priv = PRIVATE (this);
+
+ gboolean user_modifiable;
+
+
+ user_modifiable = GPOINTER_TO_INT (libslab_get_mateconf_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 00000000..077bbc72
--- /dev/null
+++ b/libslab/bookmark-agent.h
@@ -0,0 +1,94 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_agent_purge_items (BookmarkAgent *this);
+
+void bookmark_item_free (BookmarkItem *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libslab/directory-tile.c b/libslab/directory-tile.c
new file mode 100644
index 00000000..00957e81
--- /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 "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+#include <unistd.h>
+
+#include "slab-mate-util.h"
+#include "mate-utils.h"
+#include "libslab-utils.h"
+
+#define MATECONF_SEND_TO_CMD_KEY "/desktop/mate/applications/main-menu/file-area/file_send_to_cmd"
+#define MATECONF_ENABLE_DELETE_KEY_DIR "/apps/caja/preferences"
+#define MATECONF_ENABLE_DELETE_KEY MATECONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
+#define MATECONF_CONFIRM_DELETE_KEY MATECONF_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 mateconf_enable_delete_cb (MateConfClient *, guint, MateConfEntry *, 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 mateconf_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 mate_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);
+ MateConfClient *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_mateconf_value (MATECONF_ENABLE_DELETE_KEY));
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_add_dir (client, MATECONF_ENABLE_DELETE_KEY_DIR, MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ priv->mateconf_conn_id =
+ connect_mateconf_notify (MATECONF_ENABLE_DELETE_KEY, mateconf_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->mateconf_conn_id = 0;
+}
+
+static void
+directory_tile_finalize (GObject *g_object)
+{
+ DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (g_object);
+
+ MateConfClient *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 = mateconf_client_get_default ();
+
+ mateconf_client_notify_remove (client, priv->mateconf_conn_id);
+ mateconf_client_remove_dir (client, MATECONF_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 = "folder";
+
+ 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
+mateconf_enable_delete_cb (MateConfClient *client, guint conn_id, MateConfEntry *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 = mateconf_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_mateconf_value (MATECONF_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_mateconf_value (MATECONF_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 ();
+ setpgid (0, 0);
+}
+
+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_mateconf_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 00000000..374faa2b
--- /dev/null
+++ b/libslab/directory-tile.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <libslab/nameplate-tile.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libslab/document-tile.c b/libslab/document-tile.c
new file mode 100644
index 00000000..8b835600
--- /dev/null
+++ b/libslab/document-tile.c
@@ -0,0 +1,1130 @@
+/*
+ * 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 "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <gio/gio.h>
+
+#include "slab-mate-util.h"
+#include "mate-utils.h"
+#include "libslab-utils.h"
+#include "bookmark-agent.h"
+
+#define MATECONF_SEND_TO_CMD_KEY "/desktop/mate/applications/main-menu/file-area/file_send_to_cmd"
+#define MATECONF_ENABLE_DELETE_KEY_DIR "/apps/caja/preferences"
+#define MATECONF_ENABLE_DELETE_KEY MATECONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
+#define MATECONF_CONFIRM_DELETE_KEY MATECONF_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 remove_recent_item (Tile *, TileEvent *, TileAction *);
+static void purge_recent_items (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 mateconf_enable_delete_cb (MateConfClient *, guint, MateConfEntry *, 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 mateconf_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 (BOOKMARK_STORE_USER_DOCS, 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 (BookmarkStoreType bookmark_store_type, 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;
+ gchar *str;
+
+ 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);
+ priv->agent = bookmark_agent_get_instance (bookmark_store_type);
+
+ document_tile_private_setup (this);
+ TILE (this)->actions = g_new0 (TileAction *, DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS);
+ TILE (this)->n_actions = DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS;
+
+ menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
+
+ /* make open with default action */
+
+ if (priv->default_app) {
+ str = g_strdup_printf (_("Open with \"%s\""),
+ g_app_info_get_name (priv->default_app));
+ markup = g_markup_printf_escaped ("<b>%s</b>", str);
+ action = tile_action_new (TILE (this), open_with_default_trigger, markup,
+ TILE_ACTION_OPENS_NEW_WINDOW);
+ g_free (markup);
+ g_free (str);
+
+ 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 caja 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 mate_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);
+ }
+
+ if (!priv->is_bookmarked) {
+ /* clean item from menu */
+ action = tile_action_new (TILE (this), remove_recent_item, _("Remove from recent menu"), 0);
+ TILE (this)->actions[DOCUMENT_TILE_ACTION_CLEAN_ITEM] = action;
+
+ menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
+ gtk_container_add (menu_ctnr, menu_item);
+
+ /* clean all the items from menu */
+
+ action = tile_action_new (TILE (this), purge_recent_items, _("Purge all the recent items"), 0);
+ TILE (this)->actions[DOCUMENT_TILE_ACTION_CLEAN_ALL] = 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;
+
+ MateConfClient *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_mateconf_value (MATECONF_ENABLE_DELETE_KEY));
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_add_dir (client, MATECONF_ENABLE_DELETE_KEY_DIR, MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ priv->mateconf_conn_id =
+ connect_mateconf_notify (MATECONF_ENABLE_DELETE_KEY, mateconf_enable_delete_cb, this);
+
+ g_object_unref (client);
+
+ 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->mateconf_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);
+
+ MateConfClient *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 = mateconf_client_get_default ();
+
+ mateconf_client_notify_remove (client, priv->mateconf_conn_id);
+ mateconf_client_remove_dir (client, MATECONF_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;
+ MateDesktopThumbnailFactory *thumbnail_factory;
+ GIcon *icon;
+
+ 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 = "text-x-preview";
+ free_icon_id = FALSE;
+
+ goto exit;
+ }
+
+ thumbnail_factory = libslab_thumbnail_factory_get ();
+
+ icon_id = mate_desktop_thumbnail_factory_lookup (thumbnail_factory, TILE (tile)->uri, priv->modified);
+
+ if (! icon_id) {
+ icon = g_content_type_get_icon (priv->mime_type);
+ g_object_get (icon, "name", &icon_id, NULL);
+
+ g_object_unref (icon);
+ }
+
+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 (bookmark_agent_get_instance (BOOKMARK_STORE_USER_DOCS), 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
+mateconf_enable_delete_cb (MateConfClient *client, guint conn_id, MateConfEntry *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 = mateconf_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_mateconf_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
+remove_recent_item (Tile *tile, TileEvent *event, TileAction *action)
+{
+ DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+ bookmark_agent_remove_item (priv->agent, TILE (tile)->uri);
+}
+
+static void
+purge_recent_items (Tile *tile, TileEvent *event, TileAction *action)
+{
+ DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile);
+ bookmark_agent_purge_items (priv->agent);
+}
+
+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_mateconf_value (MATECONF_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_mateconf_value (MATECONF_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 00000000..c5f1f1f8
--- /dev/null
+++ b/libslab/document-tile.h
@@ -0,0 +1,70 @@
+/*
+ * 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"
+#include "bookmark-agent.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_CLEAN_ITEM 7
+#define DOCUMENT_TILE_ACTION_CLEAN_ALL 8
+#define DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS 9 /* must be last entry and equal to the number of actions */
+
+GType document_tile_get_type (void);
+
+GtkWidget *document_tile_new (BookmarkStoreType bookmark_store_type, 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libslab/double-click-detector.c b/libslab/double-click-detector.c
new file mode 100644
index 00000000..64ed58e8
--- /dev/null
+++ b/libslab/double-click-detector.c
@@ -0,0 +1,85 @@
+/*
+ * 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/gtk.h>
+
+#include "libslab-utils.h"
+
+G_DEFINE_TYPE (DoubleClickDetector, double_click_detector, G_TYPE_OBJECT);
+
+static void
+double_click_detector_class_init (DoubleClickDetectorClass * detector_class)
+{
+}
+
+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);
+}
+
+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 00000000..06217b1c
--- /dev/null
+++ b/libslab/double-click-detector.h
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __DOUBLE_CLICK_DETECTOR_H__ */
diff --git a/libslab/libslab-utils.c b/libslab/libslab-utils.c
new file mode 100644
index 00000000..80bf6472
--- /dev/null
+++ b/libslab/libslab-utils.c
@@ -0,0 +1,716 @@
+#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 <mateconf/mateconf-value.h>
+#include <gtk/gtk.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;
+}
+
+MateDesktopItem *
+libslab_mate_desktop_item_new_from_unknown_id (const gchar *id)
+{
+ MateDesktopItem *item;
+ gchar *basename;
+
+ GError *error = NULL;
+
+
+ if (! id)
+ return NULL;
+
+ item = mate_desktop_item_new_from_uri (id, 0, & error);
+
+ if (! error)
+ return item;
+ else {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_desktop_item_new_from_file (id, 0, & error);
+
+ if (! error)
+ return item;
+ else {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_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 = mate_desktop_item_new_from_basename (basename, 0, &error);
+
+ if (! error)
+ return item;
+ else {
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean
+libslab_mate_desktop_item_launch_default (MateDesktopItem *item)
+{
+ GError *error = NULL;
+
+ if (! item)
+ return FALSE;
+
+ mate_desktop_item_launch (item, NULL, MATE_DESKTOP_ITEM_LAUNCH_ONLY_ONE, & error);
+
+ if (error) {
+ g_warning ("error launching %s [%s]\n",
+ mate_desktop_item_get_location (item), error->message);
+
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gchar *
+libslab_mate_desktop_item_get_docpath (MateDesktopItem *item)
+{
+ gchar *path;
+
+ path = g_strdup (mate_desktop_item_get_localestring (item, MATE_DESKTOP_ITEM_DOC_PATH));
+
+ if (! path)
+ path = g_strdup (mate_desktop_item_get_localestring (item, ALTERNATE_DOCPATH_KEY));
+
+ return path;
+}
+
+/* Ugh, here we don't have knowledge of the screen that is being used. So, do
+ * what we can to find it.
+ */
+GdkScreen *
+libslab_get_current_screen (void)
+{
+ GdkEvent *event;
+ GdkScreen *screen = NULL;
+
+ event = gtk_get_current_event ();
+ if (event) {
+ if (event->any.window)
+ screen = gdk_drawable_get_screen (GDK_DRAWABLE (event->any.window));
+
+ gdk_event_free (event);
+ }
+
+ if (!screen)
+ screen = gdk_screen_get_default ();
+
+ return screen;
+}
+
+gboolean
+libslab_mate_desktop_item_open_help (MateDesktopItem *item)
+{
+ gchar *doc_path;
+ gchar *help_uri;
+
+ GError *error = NULL;
+
+ gboolean retval = FALSE;
+
+
+ if (! item)
+ return retval;
+
+ doc_path = libslab_mate_desktop_item_get_docpath (item);
+
+ if (doc_path) {
+ help_uri = g_strdup_printf ("ghelp:%s", doc_path);
+
+ if (!gtk_show_uri (libslab_get_current_screen (), help_uri, gtk_get_current_event_time (), &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_mateconf_value (const gchar *key)
+{
+ MateConfClient *client;
+ MateConfValue *value;
+ GError *error = NULL;
+
+ gpointer retval = NULL;
+
+ GList *list;
+ GSList *slist;
+
+ MateConfValue *value_i;
+ GSList *node;
+
+
+ client = mateconf_client_get_default ();
+ value = mateconf_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 MATECONF_VALUE_STRING:
+ retval = (gpointer) g_strdup (mateconf_value_get_string (value));
+ break;
+
+ case MATECONF_VALUE_INT:
+ retval = GINT_TO_POINTER (mateconf_value_get_int (value));
+ break;
+
+ case MATECONF_VALUE_BOOL:
+ retval = GINT_TO_POINTER (mateconf_value_get_bool (value));
+ break;
+
+ case MATECONF_VALUE_LIST:
+ list = NULL;
+ slist = mateconf_value_get_list (value);
+
+ for (node = slist; node; node = node->next) {
+ value_i = (MateConfValue *) node->data;
+
+ if (value_i->type == MATECONF_VALUE_STRING)
+ list = g_list_append (
+ list, g_strdup (
+ mateconf_value_get_string (value_i)));
+ else if (value_i->type == MATECONF_VALUE_INT)
+ list = g_list_append (
+ list, GINT_TO_POINTER (
+ mateconf_value_get_int (value_i)));
+ else
+ ;
+ }
+
+ retval = (gpointer) list;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ g_object_unref (client);
+
+ if (value)
+ mateconf_value_free (value);
+
+ return retval;
+}
+
+void
+libslab_set_mateconf_value (const gchar *key, gconstpointer data)
+{
+ MateConfClient *client;
+ MateConfValue *value;
+
+ MateConfValueType type;
+ MateConfValueType list_type;
+
+ GSList *slist = NULL;
+
+ GError *error = NULL;
+
+ MateConfValue *value_i;
+ GList *node;
+
+
+ client = mateconf_client_get_default ();
+ value = mateconf_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 == MATECONF_VALUE_LIST) ?
+ mateconf_value_get_list_type (value) : MATECONF_VALUE_INVALID);
+
+ mateconf_value_free (value);
+ value = mateconf_value_new (type);
+
+ if (type == MATECONF_VALUE_LIST)
+ mateconf_value_set_list_type (value, list_type);
+
+ switch (type) {
+ case MATECONF_VALUE_STRING:
+ mateconf_value_set_string (value, g_strdup ((gchar *) data));
+ break;
+
+ case MATECONF_VALUE_INT:
+ mateconf_value_set_int (value, GPOINTER_TO_INT (data));
+ break;
+
+ case MATECONF_VALUE_BOOL:
+ mateconf_value_set_bool (value, GPOINTER_TO_INT (data));
+ break;
+
+ case MATECONF_VALUE_LIST:
+ for (node = (GList *) data; node; node = node->next) {
+ value_i = mateconf_value_new (list_type);
+
+ if (list_type == MATECONF_VALUE_STRING)
+ mateconf_value_set_string (value_i, (const gchar *) node->data);
+ else if (list_type == MATECONF_VALUE_INT)
+ mateconf_value_set_int (value_i, GPOINTER_TO_INT (node->data));
+ else
+ g_assert_not_reached ();
+
+ slist = g_slist_append (slist, value_i);
+ }
+
+ mateconf_value_set_list_nocopy (value, slist);
+
+ break;
+
+ default:
+ break;
+ }
+
+ mateconf_client_set (client, key, value, & error);
+
+ if (error)
+ libslab_handle_g_error (&error, "%s: error setting %s", G_STRFUNC, key);
+
+exit:
+
+ mateconf_value_free (value);
+ g_object_unref (client);
+}
+
+guint
+libslab_mateconf_notify_add (const gchar *key, MateConfClientNotifyFunc callback, gpointer user_data)
+{
+ MateConfClient *client;
+ guint conn_id;
+
+ GError *error = NULL;
+
+
+ client = mateconf_client_get_default ();
+ conn_id = mateconf_client_notify_add (client, key, callback, user_data, NULL, & error);
+
+ if (error)
+ libslab_handle_g_error (
+ & error, "%s: error adding mateconf notify for (%s)", G_STRFUNC, key);
+
+ g_object_unref (client);
+
+ return conn_id;
+}
+
+void
+libslab_mateconf_notify_remove (guint conn_id)
+{
+ MateConfClient *client;
+
+ GError *error = NULL;
+
+
+ if (conn_id == 0)
+ return;
+
+ client = mateconf_client_get_default ();
+ mateconf_client_notify_remove (client, conn_id);
+
+ if (error)
+ libslab_handle_g_error (
+ & error, "%s: error removing mateconf 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)
+{
+ MateDesktopItem *d_item;
+ const gchar *categories;
+
+ gboolean is_terminal = FALSE;
+
+
+ d_item = libslab_mate_desktop_item_new_from_unknown_id (uri);
+
+ if (! d_item)
+ return FALSE;
+
+ categories = mate_desktop_item_get_string (d_item, MATE_DESKTOP_ITEM_CATEGORIES);
+
+ is_terminal = (categories && strstr (categories, DESKTOP_ITEM_TERMINAL_EMULATOR_FLAG));
+
+ mate_desktop_item_unref (d_item);
+
+ return is_terminal;
+}
+
+gboolean
+libslab_desktop_item_is_logout (const gchar *uri)
+{
+ MateDesktopItem *d_item;
+ gboolean is_logout = FALSE;
+
+
+ d_item = libslab_mate_desktop_item_new_from_unknown_id (uri);
+
+ if (! d_item)
+ return FALSE;
+
+ is_logout = strstr ("Logout", mate_desktop_item_get_string (d_item, MATE_DESKTOP_ITEM_NAME)) != NULL;
+
+ mate_desktop_item_unref (d_item);
+
+ return is_logout;
+}
+
+gboolean
+libslab_desktop_item_is_lockscreen (const gchar *uri)
+{
+ MateDesktopItem *d_item;
+ gboolean is_logout = FALSE;
+
+
+ d_item = libslab_mate_desktop_item_new_from_unknown_id (uri);
+
+ if (! d_item)
+ return FALSE;
+
+ is_logout = strstr ("Lock Screen", mate_desktop_item_get_string (d_item, MATE_DESKTOP_ITEM_NAME)) != NULL;
+
+ mate_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 MateDesktopThumbnailFactory *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 = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_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);
+}
+
+MateDesktopThumbnailFactory *
+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 00000000..10bf61b3
--- /dev/null
+++ b/libslab/libslab-utils.h
@@ -0,0 +1,46 @@
+#ifndef __LIBSLAB_UTILS_H__
+#define __LIBSLAB_UTILS_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+#include <libmate/mate-desktop-item.h>
+#define MATE_DESKTOP_USE_UNSTABLE_API 1
+#include <libmateui/mate-desktop-thumbnail.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gboolean libslab_gtk_image_set_by_id (GtkImage *image, const gchar *id);
+MateDesktopItem *libslab_mate_desktop_item_new_from_unknown_id (const gchar *id);
+gboolean libslab_mate_desktop_item_launch_default (MateDesktopItem *item);
+gchar *libslab_mate_desktop_item_get_docpath (MateDesktopItem *item);
+gboolean libslab_mate_desktop_item_open_help (MateDesktopItem *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_mateconf_value (const gchar *key);
+void libslab_set_mateconf_value (const gchar *key, gconstpointer data);
+guint libslab_mateconf_notify_add (const gchar *key, MateConfClientNotifyFunc callback, gpointer user_data);
+void libslab_mateconf_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);
+
+GdkScreen *libslab_get_current_screen (void);
+
+void libslab_thumbnail_factory_preinit (void);
+MateDesktopThumbnailFactory *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, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libslab/mate-utils.c b/libslab/mate-utils.c
new file mode 100644
index 00000000..11bcfdce
--- /dev/null
+++ b/libslab/mate-utils.c
@@ -0,0 +1,346 @@
+#include "mate-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;
+}
+
+MateDesktopItem *
+load_desktop_item_by_unknown_id (const gchar * id)
+{
+ MateDesktopItem *item;
+ GError *error = NULL;
+
+ item = mate_desktop_item_new_from_uri (id, 0, &error);
+
+ if (!error)
+ return item;
+ else
+ {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_desktop_item_new_from_file (id, 0, &error);
+
+ if (!error)
+ return item;
+ else
+ {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_desktop_item_new_from_basename (id, 0, &error);
+
+ if (!error)
+ return item;
+ else
+ {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ return NULL;
+}
+
+gpointer
+get_mateconf_value (const gchar * key)
+{
+ MateConfClient *client;
+ MateConfValue *value;
+ GError *error = NULL;
+
+ gpointer retval = NULL;
+
+ GList *list;
+ GSList *slist;
+
+ MateConfValue *value_i;
+ GSList *node;
+
+ client = mateconf_client_get_default ();
+ value = mateconf_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 MATECONF_VALUE_STRING:
+ retval = (gpointer) g_strdup (mateconf_value_get_string (value));
+ break;
+
+ case MATECONF_VALUE_INT:
+ retval = GINT_TO_POINTER (mateconf_value_get_int (value));
+ break;
+
+ case MATECONF_VALUE_BOOL:
+ retval = GINT_TO_POINTER (mateconf_value_get_bool (value));
+ break;
+
+ case MATECONF_VALUE_LIST:
+ list = NULL;
+ slist = mateconf_value_get_list (value);
+
+ for (node = slist; node; node = node->next)
+ {
+ value_i = (MateConfValue *) node->data;
+
+ if (value_i->type == MATECONF_VALUE_STRING)
+ list = g_list_append (list,
+ g_strdup (mateconf_value_get_string (value_i)));
+ else if (value_i->type == MATECONF_VALUE_INT)
+ list = g_list_append (list,
+ GINT_TO_POINTER (mateconf_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)
+ mateconf_value_free (value);
+
+ return retval;
+}
+
+void
+set_mateconf_value (const gchar * key, gconstpointer data)
+{
+ MateConfClient *client;
+ MateConfValue *value;
+
+ MateConfValueType type;
+ MateConfValueType list_type;
+
+ GSList *slist = NULL;
+
+ GError *error = NULL;
+
+ MateConfValue *value_i;
+ GList *node;
+
+ client = mateconf_client_get_default ();
+ value = mateconf_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 ==
+ MATECONF_VALUE_LIST ? mateconf_value_get_list_type (value) : MATECONF_VALUE_INVALID);
+
+ mateconf_value_free (value);
+ value = mateconf_value_new (type);
+
+ if (type == MATECONF_VALUE_LIST)
+ mateconf_value_set_list_type (value, list_type);
+
+ switch (type)
+ {
+ case MATECONF_VALUE_STRING:
+ mateconf_value_set_string (value, g_strdup ((gchar *) data));
+ break;
+
+ case MATECONF_VALUE_INT:
+ mateconf_value_set_int (value, GPOINTER_TO_INT (data));
+ break;
+
+ case MATECONF_VALUE_BOOL:
+ mateconf_value_set_bool (value, GPOINTER_TO_INT (data));
+ break;
+
+ case MATECONF_VALUE_LIST:
+ for (node = (GList *) data; node; node = node->next)
+ {
+ value_i = mateconf_value_new (list_type);
+
+ if (list_type == MATECONF_VALUE_STRING)
+ mateconf_value_set_string (value_i, (const gchar *) node->data);
+ else if (list_type == MATECONF_VALUE_INT)
+ mateconf_value_set_int (value_i, GPOINTER_TO_INT (node->data));
+ else
+ g_assert_not_reached ();
+
+ slist = g_slist_append (slist, value_i);
+ }
+
+ mateconf_value_set_list_nocopy (value, slist);
+
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ mateconf_client_set (client, key, value, &error);
+
+ if (error)
+ handle_g_error (&error, "%s: error setting %s", G_STRFUNC, key);
+
+ exit:
+
+ mateconf_value_free (value);
+ g_object_unref (client);
+}
+
+guint
+connect_mateconf_notify (const gchar * key, MateConfClientNotifyFunc cb, gpointer user_data)
+{
+ MateConfClient *client;
+ guint conn_id;
+
+ GError *error = NULL;
+
+ client = mateconf_client_get_default ();
+ conn_id = mateconf_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, "mate-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/mate-utils.h b/libslab/mate-utils.h
new file mode 100644
index 00000000..fcc7e586
--- /dev/null
+++ b/libslab/mate-utils.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __MATE_UTILS_H__
+#define __MATE_UTILS_H__
+
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+#include <libmate/mate-desktop-item.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gboolean load_image_by_id (GtkImage * image, GtkIconSize size,
+ const gchar * image_id);
+MateDesktopItem *load_desktop_item_by_unknown_id (const gchar * id);
+gpointer get_mateconf_value (const gchar * key);
+void set_mateconf_value (const gchar * key, gconstpointer data);
+guint connect_mateconf_notify (const gchar * key, MateConfClientNotifyFunc cb, gpointer user_data);
+void handle_g_error (GError ** error, const gchar * user_format, ...);
+GtkWidget *get_main_menu_section_header (const gchar * markup);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __MATE_UTILS_H__ */
diff --git a/libslab/nameplate-tile.c b/libslab/nameplate-tile.c
new file mode 100644
index 00000000..f3f45165
--- /dev/null
+++ b/libslab/nameplate-tile.c
@@ -0,0 +1,290 @@
+/*
+ * 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)
+{
+ 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 00000000..00a1e17a
--- /dev/null
+++ b/libslab/nameplate-tile.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <libslab/tile.h>
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libslab/nld-marshal.list b/libslab/nld-marshal.list
new file mode 100644
index 00000000..158cf7a7
--- /dev/null
+++ b/libslab/nld-marshal.list
@@ -0,0 +1 @@
+VOID:INT,STRING
diff --git a/libslab/search-bar.c b/libslab/search-bar.c
new file mode 100644
index 00000000..8e3f465b
--- /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 "config.h"
+
+#include "search-entry.h"
+#include "search-context-picker.h"
+#include "nld-marshal.h"
+
+#include <glib/gi18n-lib.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 00000000..10d00908
--- /dev/null
+++ b/libslab/search-bar.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 __NLD_SEARCH_BAR_H__
+#define __NLD_SEARCH_BAR_H__
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __NLD_SEARCH_BAR_H__ */
diff --git a/libslab/search-context-picker.c b/libslab/search-context-picker.c
new file mode 100644
index 00000000..449c6297
--- /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 00000000..9480c249
--- /dev/null
+++ b/libslab/search-context-picker.h
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#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 00000000..f72efff1
--- /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 00000000..7ef02e0e
--- /dev/null
+++ b/libslab/search-entry.c
@@ -0,0 +1,150 @@
+/*
+ * 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)
+ {
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ width = gdk_window_get_width(event->window);
+ height = gdk_window_get_height(event->window);
+ #else
+ gdk_drawable_get_size(event->window, &width, &height);
+ #endif
+ 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 00000000..0cb07db2
--- /dev/null
+++ b/libslab/search-entry.h
@@ -0,0 +1,54 @@
+/*
+ * 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/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __NLD_SEARCH_ENTRY_H__ */
diff --git a/libslab/shell-window.c b/libslab/shell-window.c
new file mode 100644
index 00000000..23656a50
--- /dev/null
+++ b/libslab/shell-window.c
@@ -0,0 +1,147 @@
+/*
+ * 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/gtk.h>
+
+#include "app-resizer.h"
+
+static void shell_window_class_init (ShellWindowClass *);
+static void shell_window_init (ShellWindow *);
+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
+
+G_DEFINE_TYPE (ShellWindow, shell_window, GTK_TYPE_FRAME);
+
+static void
+shell_window_class_init (ShellWindowClass * klass)
+{
+}
+
+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);
+}
+
+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 00000000..9ce2063d
--- /dev/null
+++ b/libslab/shell-window.h
@@ -0,0 +1,69 @@
+/*
+ * 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/gtk.h>
+#include <libmate/mate-desktop-item.h>
+
+#include <libslab/app-shell.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __SHELL_WINDOW_H__ */
diff --git a/libslab/slab-mate-util.c b/libslab/slab-mate-util.c
new file mode 100644
index 00000000..4ca71e3f
--- /dev/null
+++ b/libslab/slab-mate-util.c
@@ -0,0 +1,471 @@
+/*
+ * 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-mate-util.h"
+#include "libslab-utils.h"
+
+#include <mateconf/mateconf-client.h>
+#include <gio/gio.h>
+#include <string.h>
+
+gboolean
+get_slab_mateconf_bool (const gchar * key)
+{
+ MateConfClient *mateconf_client;
+ GError *error;
+
+ gboolean value;
+
+ mateconf_client = mateconf_client_get_default ();
+ error = NULL;
+
+ value = mateconf_client_get_bool (mateconf_client, key, &error);
+
+ g_object_unref (mateconf_client);
+
+ if (error)
+ {
+ g_warning ("error accessing %s [%s]\n", key, error->message);
+ g_error_free (error);
+ }
+
+ return value;
+}
+
+gint
+get_slab_mateconf_int (const gchar * key)
+{
+ MateConfClient *mateconf_client;
+ GError *error;
+
+ gint value;
+
+ mateconf_client = mateconf_client_get_default ();
+ error = NULL;
+
+ value = mateconf_client_get_int (mateconf_client, key, &error);
+
+ g_object_unref (mateconf_client);
+ if (error)
+ {
+ g_warning ("error accessing %s [%s]\n", key, error->message);
+ g_error_free (error);
+ }
+
+ return value;
+}
+
+gchar *
+get_slab_mateconf_string (const gchar * key)
+{
+ MateConfClient *mateconf_client;
+ GError *error;
+
+ gchar *value;
+
+ mateconf_client = mateconf_client_get_default ();
+ error = NULL;
+
+ value = mateconf_client_get_string (mateconf_client, key, &error);
+
+ g_object_unref (mateconf_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_mateconf_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_mateconf_slist (const gchar * key)
+{
+ MateConfClient *mateconf_client;
+ GError *error;
+
+ GSList *value;
+
+ mateconf_client = mateconf_client_get_default ();
+ error = NULL;
+
+ value = mateconf_client_get_list (mateconf_client, key, MATECONF_VALUE_STRING, &error);
+
+ g_object_unref (mateconf_client);
+ if (error)
+ {
+ g_warning ("error accessing %s [%s]\n", key, error->message);
+
+ g_error_free (error);
+ }
+
+ return value;
+}
+
+MateDesktopItem *
+load_desktop_item_from_mateconf_key (const gchar * key)
+{
+ MateDesktopItem *item;
+ gchar *id = get_slab_mateconf_string (key);
+
+ if (!id)
+ return NULL;
+
+ item = load_desktop_item_from_unknown (id);
+ g_free (id);
+ return item;
+}
+
+MateDesktopItem *
+load_desktop_item_from_unknown (const gchar *id)
+{
+ MateDesktopItem *item;
+ gchar *basename;
+
+ GError *error = NULL;
+
+
+ item = mate_desktop_item_new_from_uri (id, 0, &error);
+
+ if (! error)
+ return item;
+ else {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_desktop_item_new_from_file (id, 0, &error);
+
+ if (! error)
+ return item;
+ else {
+ g_error_free (error);
+ error = NULL;
+ }
+
+ item = mate_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 = mate_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 (MateDesktopItem * 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 (mate_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 (MateDesktopItem * desktop_item)
+{
+ GError *error = NULL;
+
+ if (!desktop_item)
+ return FALSE;
+
+ mate_desktop_item_launch (desktop_item, NULL, MATE_DESKTOP_ITEM_LAUNCH_ONLY_ONE, &error);
+
+ if (error)
+ {
+ g_warning ("error launching %s [%s]\n",
+ mate_desktop_item_get_location (desktop_item), error->message);
+
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+open_desktop_item_help (MateDesktopItem * desktop_item)
+{
+ const gchar *doc_path;
+ gchar *help_uri;
+
+ GError *error;
+
+ if (!desktop_item)
+ return FALSE;
+
+ doc_path = mate_desktop_item_get_string (desktop_item, "DocPath");
+
+ if (doc_path)
+ {
+ help_uri = g_strdup_printf ("ghelp:%s", doc_path);
+
+ error = NULL;
+ if (!gtk_show_uri (libslab_get_current_screen (), help_uri, gtk_get_current_event_time (), &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 (MateDesktopItem * desktop_item)
+{
+ return desktop_uri_is_in_main_menu (mate_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_mateconf_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_mateconf_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-mate-util.h b/libslab/slab-mate-util.h
new file mode 100644
index 00000000..d988f48b
--- /dev/null
+++ b/libslab/slab-mate-util.h
@@ -0,0 +1,80 @@
+/*
+ * 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_MATE_UTIL_H__
+#define __SLAB_MATE_UTIL_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <libmate/mate-desktop-item.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SLAB_APPLICATION_BROWSER_KEY "/desktop/mate/applications/main-menu/application_browser"
+#define SLAB_SYSTEM_LIST_KEY "/desktop/mate/applications/main-menu/system_list"
+#define SLAB_FILE_BROWSER_KEY "/desktop/mate/applications/main-menu/file_browser"
+#define SLAB_SYSTEM_MONITOR_KEY "/desktop/mate/applications/main-menu/system_monitor"
+#define SLAB_NETWORK_CONFIG_TOOL_KEY "/desktop/mate/applications/main-menu/network_config_tool"
+#define SLAB_NETWORK_CONFIG_TOOL_NM_KEY "/desktop/mate/applications/main-menu/network_config_tool_nm"
+#define SLAB_URGENT_CLOSE_KEY "/desktop/mate/applications/main-menu/urgent_close"
+#define SLAB_LOCK_SCREEN_PRIORITY_KEY "/desktop/mate/applications/main-menu/lock_screen_priority"
+#define SLAB_MAIN_MENU_REORDERING_KEY "/desktop/mate/applications/main-menu/main_menu_reordering"
+#define SLAB_UPGRADE_PACKAGE_KEY "/desktop/mate/applications/main-menu/upgrade_package_command"
+#define SLAB_UNINSTALL_PACKAGE_KEY "/desktop/mate/applications/main-menu/uninstall_package_command"
+#define SLAB_USER_SPECIFIED_APPS_KEY "/desktop/mate/applications/main-menu/file-area/user_specified_apps"
+#define SLAB_APPLICATION_USE_DB_KEY "/desktop/mate/applications/main-menu/file-area/app_use_db"
+#define SLAB_FILE_ITEM_LIMIT "/desktop/mate/applications/main-menu/file-area/item_limit"
+#define SLAB_FILE_BLACKLIST "/desktop/mate/applications/main-menu/file-area/file_blacklist"
+#define SLAB_FILE_MANAGER_OPEN_CMD "/desktop/mate/applications/main-menu/file-area/file_mgr_open_cmd"
+#define SLAB_FILE_SEND_TO_CMD "/desktop/mate/applications/main-menu/file-area/file_send_to_cmd"
+
+gboolean get_slab_mateconf_bool (const gchar * key);
+gint get_slab_mateconf_int (const gchar * key);
+GSList *get_slab_mateconf_slist (const gchar * key);
+void free_slab_mateconf_slist_of_strings (GSList * list);
+void free_list_of_strings (GList * list);
+gchar *get_slab_mateconf_string (const gchar * key);
+
+MateDesktopItem *load_desktop_item_from_mateconf_key (const gchar * key);
+MateDesktopItem *load_desktop_item_from_unknown (const gchar * id);
+
+gchar *get_package_name_from_desktop_item (MateDesktopItem * desktop_item);
+
+gboolean open_desktop_item_exec (MateDesktopItem * desktop_item);
+gboolean open_desktop_item_help (MateDesktopItem * desktop_item);
+
+gboolean desktop_item_is_in_main_menu (MateDesktopItem * 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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __SLAB_MATE_UTIL_H__ */
diff --git a/libslab/slab-section.c b/libslab/slab-section.c
new file mode 100644
index 00000000..6fbf7006
--- /dev/null
+++ b/libslab/slab-section.c
@@ -0,0 +1,194 @@
+/*
+ * 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"
+
+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 00000000..a6f9c1d7
--- /dev/null
+++ b/libslab/slab-section.h
@@ -0,0 +1,75 @@
+/*
+ * 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/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __SLAB_SECTION_H__ */
diff --git a/libslab/slab.h b/libslab/slab.h
new file mode 100644
index 00000000..cacf7a9c
--- /dev/null
+++ b/libslab/slab.h
@@ -0,0 +1,45 @@
+/*
+ * 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_H__
+#define __SLAB_H__
+
+#include <libslab/app-resizer.h>
+#include <libslab/app-shell.h>
+#include <libslab/application-tile.h>
+#include <libslab/bookmark-agent.h>
+#include <libslab/directory-tile.h>
+#include <libslab/document-tile.h>
+#include <libslab/double-click-detector.h>
+#include <libslab/mate-utils.h>
+#include <libslab/libslab-utils.h>
+#include <libslab/nameplate-tile.h>
+#include <libslab/search-bar.h>
+#include <libslab/search-context-picker.h>
+#include <libslab/search-entry.h>
+#include <libslab/shell-window.h>
+#include <libslab/slab-mate-util.h>
+#include <libslab/slab-section.h>
+#include <libslab/system-tile.h>
+#include <libslab/tile.h>
+
+#endif /* __SLAB_H__ */
+
diff --git a/libslab/system-tile.c b/libslab/system-tile.c
new file mode 100644
index 00000000..ce64cebe
--- /dev/null
+++ b/libslab/system-tile.c
@@ -0,0 +1,285 @@
+/*
+ * 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 "config.h"
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <mateconf/mateconf-client.h>
+
+#include "bookmark-agent.h"
+#include "slab-mate-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 {
+ MateDesktopItem *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;
+
+ MateDesktopItem *desktop_item = NULL;
+ gchar *image_id = NULL;
+ gchar *header_txt = NULL;
+
+ gchar *markup;
+
+ AtkObject *accessible = NULL;
+
+
+ desktop_item = libslab_mate_desktop_item_new_from_unknown_id (desktop_item_id);
+
+ if (desktop_item) {
+ image_id = g_strdup (mate_desktop_item_get_localestring (desktop_item, "Icon"));
+ uri = g_strdup (mate_desktop_item_get_location (desktop_item));
+
+ if (title)
+ header_txt = g_strdup (title);
+ else
+ header_txt = g_strdup (
+ mate_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);
+ mate_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 00000000..a44012d6
--- /dev/null
+++ b/libslab/system-tile.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <libslab/nameplate-tile.h>
+
+#include <libmate/mate-desktop-item.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libslab/themed-icon.c b/libslab/themed-icon.c
new file mode 100644
index 00000000..b4259c4f
--- /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 "mate-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 00000000..1ddad004
--- /dev/null
+++ b/libslab/themed-icon.h
@@ -0,0 +1,57 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __THEMED_ICON_H__ */
diff --git a/libslab/tile-action.c b/libslab/tile-action.c
new file mode 100644
index 00000000..8ad5ff83
--- /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 00000000..870944d9
--- /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 00000000..e06366b7
--- /dev/null
+++ b/libslab/tile.h
@@ -0,0 +1,147 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif