diff options
Diffstat (limited to 'libslab')
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 %l and %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 |