summaryrefslogtreecommitdiff
path: root/capplet
diff options
context:
space:
mode:
Diffstat (limited to 'capplet')
-rw-r--r--capplet/Makefile.am33
-rw-r--r--capplet/Makefile.in639
-rw-r--r--capplet/gsm-app-dialog.c544
-rw-r--r--capplet/gsm-app-dialog.h70
-rw-r--r--capplet/gsm-properties-dialog.c819
-rw-r--r--capplet/gsm-properties-dialog.h61
-rw-r--r--capplet/gsp-app-manager.c593
-rw-r--r--capplet/gsp-app-manager.h85
-rw-r--r--capplet/gsp-app.c1050
-rw-r--r--capplet/gsp-app.h109
-rw-r--r--capplet/gsp-keyfile.c149
-rw-r--r--capplet/gsp-keyfile.h67
-rw-r--r--capplet/main.c102
13 files changed, 4321 insertions, 0 deletions
diff --git a/capplet/Makefile.am b/capplet/Makefile.am
new file mode 100644
index 0000000..cab50d1
--- /dev/null
+++ b/capplet/Makefile.am
@@ -0,0 +1,33 @@
+bin_PROGRAMS = mate-session-properties
+
+AM_CPPFLAGS = \
+ $(SESSION_PROPERTIES_CFLAGS) \
+ $(MATECONF_CFLAGS) \
+ -I$(top_srcdir)/egg \
+ -I$(top_srcdir)/mate-session \
+ -DLOCALE_DIR=\""$(datadir)/locale"\" \
+ -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+AM_CFLAGS = $(WARN_CFLAGS)
+
+mate_session_properties_SOURCES = \
+ main.c \
+ gsm-properties-dialog.h \
+ gsm-properties-dialog.c \
+ gsm-app-dialog.h \
+ gsm-app-dialog.c \
+ gsp-app.h \
+ gsp-app.c \
+ gsp-app-manager.h \
+ gsp-app-manager.c \
+ gsp-keyfile.h \
+ gsp-keyfile.c
+
+mate_session_properties_LDADD = \
+ $(SESSION_PROPERTIES_LIBS) \
+ $(top_builddir)/egg/libeggdesktopfile.la \
+ $(top_builddir)/mate-session/libgsmutil.la \
+ $(MATECONF_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/capplet/Makefile.in b/capplet/Makefile.in
new file mode 100644
index 0000000..7554785
--- /dev/null
+++ b/capplet/Makefile.in
@@ -0,0 +1,639 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+
+# 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.
+
+
+pkgdatadir = $(datadir)/@[email protected]
+pkgincludedir = $(includedir)/@[email protected]
+pkglibdir = $(libdir)/@[email protected]
+pkglibexecdir = $(libexecdir)/@[email protected]
+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 = @[email protected]
+host_triplet = @[email protected]
+bin_PROGRAMS = mate-session-properties$(EXEEXT)
+subdir = capplet
+DIST_COMMON = $(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)/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__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_mate_session_properties_OBJECTS = main.$(OBJEXT) \
+ gsm-properties-dialog.$(OBJEXT) gsm-app-dialog.$(OBJEXT) \
+ gsp-app.$(OBJEXT) gsp-app-manager.$(OBJEXT) \
+ gsp-keyfile.$(OBJEXT)
+mate_session_properties_OBJECTS = \
+ $(am_mate_session_properties_OBJECTS)
+am__DEPENDENCIES_1 =
+mate_session_properties_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(top_builddir)/egg/libeggdesktopfile.la \
+ $(top_builddir)/mate-session/libgsmutil.la \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+DEFAULT_INCLUDES = [email protected][email protected] -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 " [email protected];
+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 [email protected]
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " [email protected];
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " [email protected];
+SOURCES = $(mate_session_properties_SOURCES)
+DIST_SOURCES = $(mate_session_properties_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @[email protected]
+ACLOCAL_AMFLAGS = @[email protected]
+ALL_LINGUAS = @[email protected]
+AM_DEFAULT_VERBOSITY = @[email protected]
+AUTOCONF = @[email protected]
+AUTOHEADER = @[email protected]
+AUTOMAKE = @[email protected]
+CATALOGS = @[email protected]
+CATOBJEXT = @[email protected]
+CCDEPMODE = @[email protected]
+CPPFLAGS = @[email protected]
+CYGPATH_W = @[email protected]
+DATADIRNAME = @[email protected]
+DBUS_GLIB_CFLAGS = @[email protected]
+DBUS_GLIB_LIBS = @[email protected]
+DEFAULT_WM = @[email protected]
+DISABLE_DEPRECATED = @[email protected]
+DISABLE_DEPRECATED_CFLAGS = @[email protected]
+DLLTOOL = @[email protected]
+DSYMUTIL = @[email protected]
+DUMPBIN = @[email protected]
+EGG_SMCLIENT_CFLAGS = @[email protected]
+EGG_SMCLIENT_LIBS = @[email protected]
+EXECINFO_LIBS = @[email protected]
+GETTEXT_PACKAGE = @[email protected]
+GLIB_GENMARSHAL = @[email protected]
+GMOFILES = @[email protected]
+GMSGFMT = @[email protected]
+HAVE_XRENDER = @[email protected]
+HAVE_XTEST = @[email protected]
+ICE_CFLAGS = @[email protected]
+ICE_LIBS = @[email protected]
+INSTALL = @[email protected]
+INSTALL_DATA = @[email protected]
+INSTALL_PROGRAM = @[email protected]
+INSTALL_SCRIPT = @[email protected]
+INSTALL_STRIP_PROGRAM = @[email protected]
+INSTOBJEXT = @[email protected]
+INTLLIBS = @[email protected]
+INTLTOOL_EXTRACT = @[email protected]
+INTLTOOL_MERGE = @[email protected]
+INTLTOOL_PERL = @[email protected]
+INTLTOOL_UPDATE = @[email protected]
+LDFLAGS = @[email protected]
+LIBOBJS = @[email protected]
+LIBTOOL = @[email protected]
+LTLIBOBJS = @[email protected]
+MAKEINFO = @[email protected]
+MANIFEST_TOOL = @[email protected]
+MATECONFTOOL = @[email protected]
+MATECONF_CFLAGS = @[email protected]
+MATECONF_LIBS = @[email protected]
+MATECONF_SANITY_CHECK = @[email protected]
+MATECONF_SCHEMA_CONFIG_SOURCE = @[email protected]
+MATECONF_SCHEMA_FILE_DIR = @[email protected]
+MATE_SESSION_CFLAGS = @[email protected]
+MATE_SESSION_LIBS = @[email protected]
+MKDIR_P = @[email protected]
+MKINSTALLDIRS = @[email protected]
+MSGFMT_OPTS = @[email protected]
+MSGMERGE = @[email protected]
+OBJDUMP = @[email protected]
+OTOOL64 = @[email protected]
+PACKAGE = @[email protected]
+PACKAGE_BUGREPORT = @PACKAGE_BUGR[email protected]
+PACKAGE_NAME = @[email protected]
+PACKAGE_STRING = @[email protected]
+PACKAGE_TARNAME = @[email protected]
+PACKAGE_URL = @[email protected]
+PACKAGE_VERSION = @[email protected]
+PATH_SEPARATOR = @[email protected]
+PKG_CONFIG = @[email protected]
+PKG_CONFIG_LIBDIR = @[email protected]
+PKG_CONFIG_PATH = @[email protected]
+POFILES = @[email protected]
+PO_IN_DATADIR_FALSE = @[email protected]
+PO_IN_DATADIR_TRUE = @[email protected]
+REBUILD = @[email protected]
+SESSION_PROPERTIES_CFLAGS = @[email protected]
+SESSION_PROPERTIES_LIBS = @[email protected]
+SET_MAKE = @[email protected]
+SM_CFLAGS = @[email protected]
+SM_LIBS = @[email protected]
+USE_NLS = @[email protected]
+VERSION = @[email protected]
+WARN_CFLAGS = @[email protected]
+XEXT_CFLAGS = @[email protected]
+XEXT_LIBS = @[email protected]
+XGETTEXT = @[email protected]
+XRENDER_CFLAGS = @[email protected]
+XRENDER_LIBS = @[email protected]
+XSLTPROC = @[email protected]
+XTEST_CFLAGS = @[email protected]
+XTEST_LIBS = @[email protected]
+X_CFLAGS = @[email protected]
+X_EXTRA_LIBS = @[email protected]
+X_PRE_LIBS = @[email protected]
+abs_builddir = @[email protected]
+abs_srcdir = @[email protected]
+abs_top_builddir = @[email protected]
+abs_top_srcdir = @[email protected]
+ac_ct_AR = @[email protected]
+ac_ct_CC = @[email protected]
+ac_ct_DUMPBIN = @[email protected]
+am__include = @[email protected]
+am__leading_dot = @[email protected]
+am__quote = @[email protected]
+am__tar = @[email protected]
+am__untar = @[email protected]
+build_alias = @[email protected]
+build_cpu = @[email protected]
+build_os = @[email protected]
+build_vendor = @[email protected]
+builddir = @[email protected]
+datadir = @[email protected]
+datarootdir = @[email protected]
+exec_prefix = @[email protected]
+host_alias = @[email protected]
+host_cpu = @[email protected]
+host_os = @[email protected]
+host_vendor = @[email protected]
+htmldir = @[email protected]
+includedir = @[email protected]
+infodir = @[email protected]
+install_sh = @[email protected]
+libexecdir = @[email protected]
+localedir = @[email protected]
+localstatedir = @[email protected]
+mkdir_p = @[email protected]
+oldincludedir = @[email protected]
+program_transform_name = @program_tra[email protected]
+sbindir = @[email protected]
+sharedstatedir = @[email protected]
+sysconfdir = @[email protected]
+target_alias = @[email protected]
+top_build_prefix = @[email protected]
+top_builddir = @[email protected]
+top_srcdir = @[email protected]
+AM_CPPFLAGS = \
+ $(SESSION_PROPERTIES_CFLAGS) \
+ $(MATECONF_CFLAGS) \
+ -I$(top_srcdir)/egg \
+ -I$(top_srcdir)/mate-session \
+ -DLOCALE_DIR=\""$(datadir)/locale"\" \
+ -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+AM_CFLAGS = $(WARN_CFLAGS)
+mate_session_properties_SOURCES = \
+ main.c \
+ gsm-properties-dialog.h \
+ gsm-properties-dialog.c \
+ gsm-app-dialog.h \
+ gsm-app-dialog.c \
+ gsp-app.h \
+ gsp-app.c \
+ gsp-app-manager.h \
+ gsp-app-manager.c \
+ gsp-keyfile.h \
+ gsp-keyfile.c
+
+mate_session_properties_LDADD = \
+ $(SESSION_PROPERTIES_LIBS) \
+ $(top_builddir)/egg/libeggdesktopfile.la \
+ $(top_builddir)/mate-session/libgsmutil.la \
+ $(MATECONF_LIBS)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @[email protected] $(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 [email protected]; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu capplet/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu capplet/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)/[email protected] $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/[email protected] $(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: @[email protected] $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @[email protected] $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+mate-session-properties$(EXEEXT): $(mate_session_properties_OBJECTS) $(mate_session_properties_DEPENDENCIES)
+ @rm -f mate-session-properties$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(mate_session_properties_OBJECTS) $(mate_session_properties_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+
+.c.o:
[email protected][email protected] $(AM_V_CC)$(COMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] $<
[email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+
+.c.obj:
[email protected][email protected] $(AM_V_CC)$(COMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] `$(CYGPATH_W) '$<'`
[email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
[email protected][email protected] $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
[email protected][email protected] $(AM_V_CC)$(LTCOMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] $<
[email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+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) \
+ "[email protected]" $$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: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: 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:
+
+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."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool 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-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+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-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS 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-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-binPROGRAMS
+
+
+-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/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c
new file mode 100644
index 0000000..f130e0c
--- /dev/null
+++ b/capplet/gsm-app-dialog.c
@@ -0,0 +1,544 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gsm-util.h"
+
+#include "gsm-app-dialog.h"
+
+#define GTKBUILDER_FILE "session-properties.ui"
+
+#define CAPPLET_NAME_ENTRY_WIDGET_NAME "session_properties_name_entry"
+#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "session_properties_command_entry"
+#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry"
+#define CAPPLET_BROWSE_WIDGET_NAME "session_properties_browse_button"
+
+
+#define GSM_APP_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogPrivate))
+
+struct GsmAppDialogPrivate
+{
+ GtkWidget *name_entry;
+ GtkWidget *command_entry;
+ GtkWidget *comment_entry;
+ GtkWidget *browse_button;
+ char *name;
+ char *command;
+ char *comment;
+};
+
+static void gsm_app_dialog_class_init (GsmAppDialogClass *klass);
+static void gsm_app_dialog_init (GsmAppDialog *app_dialog);
+static void gsm_app_dialog_finalize (GObject *object);
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_COMMAND,
+ PROP_COMMENT
+};
+
+G_DEFINE_TYPE (GsmAppDialog, gsm_app_dialog, GTK_TYPE_DIALOG)
+
+static char *
+make_exec_uri (const char *exec)
+{
+ GString *str;
+ const char *c;
+
+ if (exec == NULL) {
+ return g_strdup ("");
+ }
+
+ if (strchr (exec, ' ') == NULL) {
+ return g_strdup (exec);
+ }
+
+ str = g_string_new_len (NULL, strlen (exec));
+
+ str = g_string_append_c (str, '"');
+ for (c = exec; *c != '\0'; c++) {
+ /* FIXME: GKeyFile will add an additional backslach so we'll
+ * end up with toto\\" instead of toto\"
+ * We could use g_key_file_set_value(), but then we don't
+ * benefit from the other escaping that glib is doing...
+ */
+ if (*c == '"') {
+ str = g_string_append (str, "\\\"");
+ } else {
+ str = g_string_append_c (str, *c);
+ }
+ }
+ str = g_string_append_c (str, '"');
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+on_browse_button_clicked (GtkWidget *widget,
+ GsmAppDialog *dialog)
+{
+ GtkWidget *chooser;
+ int response;
+
+ chooser = gtk_file_chooser_dialog_new ("",
+ GTK_WINDOW (dialog),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_window_set_transient_for (GTK_WINDOW (chooser),
+ GTK_WINDOW (dialog));
+
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
+
+ gtk_window_set_title (GTK_WINDOW (chooser), _("Select Command"));
+
+ gtk_widget_show (chooser);
+
+ response = gtk_dialog_run (GTK_DIALOG (chooser));
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ char *text;
+ char *uri;
+
+ text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+
+ uri = make_exec_uri (text);
+
+ g_free (text);
+
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), uri);
+
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (chooser);
+}
+
+static void
+on_entry_activate (GtkEntry *entry,
+ GsmAppDialog *dialog)
+{
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+static void
+setup_dialog (GsmAppDialog *dialog)
+{
+ GtkWidget *content_area;
+ GtkWidget *widget;
+ GtkBuilder *xml;
+ GError *error;
+
+ xml = gtk_builder_new ();
+ gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE);
+
+ error = NULL;
+ if (!gtk_builder_add_from_file (xml,
+ GTKBUILDER_DIR "/" GTKBUILDER_FILE,
+ &error)) {
+ if (error) {
+ g_warning ("Could not load capplet UI file: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Could not load capplet UI file.");
+ }
+ }
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ widget = GTK_WIDGET (gtk_builder_get_object (xml, "main-table"));
+ gtk_container_add (GTK_CONTAINER (content_area), widget);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "mate-session-properties");
+
+ g_object_set (dialog,
+ "allow-shrink", FALSE,
+ "allow-grow", FALSE,
+ NULL);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+ if (dialog->priv->name == NULL
+ && dialog->priv->command == NULL
+ && dialog->priv->comment == NULL) {
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Add Startup Program"));
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_ADD, GTK_RESPONSE_OK);
+ } else {
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Startup Program"));
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+ }
+
+ dialog->priv->name_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME));
+ g_signal_connect (dialog->priv->name_entry,
+ "activate",
+ G_CALLBACK (on_entry_activate),
+ dialog);
+ if (dialog->priv->name != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), dialog->priv->name);
+ }
+
+ dialog->priv->browse_button = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_BROWSE_WIDGET_NAME));
+ g_signal_connect (dialog->priv->browse_button,
+ "clicked",
+ G_CALLBACK (on_browse_button_clicked),
+ dialog);
+
+ dialog->priv->command_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME));
+ g_signal_connect (dialog->priv->command_entry,
+ "activate",
+ G_CALLBACK (on_entry_activate),
+ dialog);
+ if (dialog->priv->command != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), dialog->priv->command);
+ }
+
+ dialog->priv->comment_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME));
+ g_signal_connect (dialog->priv->comment_entry,
+ "activate",
+ G_CALLBACK (on_entry_activate),
+ dialog);
+ if (dialog->priv->comment != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), dialog->priv->comment);
+ }
+
+ if (xml != NULL) {
+ g_object_unref (xml);
+ }
+}
+
+static GObject *
+gsm_app_dialog_constructor (GType type,
+ guint n_construct_app,
+ GObjectConstructParam *construct_app)
+{
+ GsmAppDialog *dialog;
+
+ dialog = GSM_APP_DIALOG (G_OBJECT_CLASS (gsm_app_dialog_parent_class)->constructor (type,
+ n_construct_app,
+ construct_app));
+
+ setup_dialog (dialog);
+
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+
+ return G_OBJECT (dialog);
+}
+
+static void
+gsm_app_dialog_dispose (GObject *object)
+{
+ GsmAppDialog *dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_APP_DIALOG (object));
+
+ dialog = GSM_APP_DIALOG (object);
+
+ g_free (dialog->priv->name);
+ dialog->priv->name = NULL;
+ g_free (dialog->priv->command);
+ dialog->priv->command = NULL;
+ g_free (dialog->priv->comment);
+ dialog->priv->comment = NULL;
+
+ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->dispose (object);
+}
+
+static void
+gsm_app_dialog_set_name (GsmAppDialog *dialog,
+ const char *name)
+{
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+ g_free (dialog->priv->name);
+
+ dialog->priv->name = g_strdup (name);
+ g_object_notify (G_OBJECT (dialog), "name");
+}
+
+static void
+gsm_app_dialog_set_command (GsmAppDialog *dialog,
+ const char *name)
+{
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+ g_free (dialog->priv->command);
+
+ dialog->priv->command = g_strdup (name);
+ g_object_notify (G_OBJECT (dialog), "command");
+}
+
+static void
+gsm_app_dialog_set_comment (GsmAppDialog *dialog,
+ const char *name)
+{
+ g_return_if_fail (GSM_IS_APP_DIALOG (dialog));
+
+ g_free (dialog->priv->comment);
+
+ dialog->priv->comment = g_strdup (name);
+ g_object_notify (G_OBJECT (dialog), "comment");
+}
+
+const char *
+gsm_app_dialog_get_name (GsmAppDialog *dialog)
+{
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry));
+}
+
+const char *
+gsm_app_dialog_get_command (GsmAppDialog *dialog)
+{
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry));
+}
+
+const char *
+gsm_app_dialog_get_comment (GsmAppDialog *dialog)
+{
+ g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL);
+ return gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry));
+}
+
+static void
+gsm_app_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsmAppDialog *dialog = GSM_APP_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ gsm_app_dialog_set_name (dialog, g_value_get_string (value));
+ break;
+ case PROP_COMMAND:
+ gsm_app_dialog_set_command (dialog, g_value_get_string (value));
+ break;
+ case PROP_COMMENT:
+ gsm_app_dialog_set_comment (dialog, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_app_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsmAppDialog *dialog = GSM_APP_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, dialog->priv->name);
+ break;
+ case PROP_COMMAND:
+ g_value_set_string (value, dialog->priv->command);
+ break;
+ case PROP_COMMENT:
+ g_value_set_string (value, dialog->priv->comment);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_app_dialog_class_init (GsmAppDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gsm_app_dialog_get_property;
+ object_class->set_property = gsm_app_dialog_set_property;
+ object_class->constructor = gsm_app_dialog_constructor;
+ object_class->dispose = gsm_app_dialog_dispose;
+ object_class->finalize = gsm_app_dialog_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "name",
+ "name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_COMMAND,
+ g_param_spec_string ("command",
+ "command",
+ "command",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_COMMENT,
+ g_param_spec_string ("comment",
+ "comment",
+ "comment",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GsmAppDialogPrivate));
+}
+
+static void
+gsm_app_dialog_init (GsmAppDialog *dialog)
+{
+
+ dialog->priv = GSM_APP_DIALOG_GET_PRIVATE (dialog);
+}
+
+static void
+gsm_app_dialog_finalize (GObject *object)
+{
+ GsmAppDialog *dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_APP_DIALOG (object));
+
+ dialog = GSM_APP_DIALOG (object);
+
+ g_return_if_fail (dialog->priv != NULL);
+
+ G_OBJECT_CLASS (gsm_app_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gsm_app_dialog_new (const char *name,
+ const char *command,
+ const char *comment)
+{
+ GObject *object;
+
+ object = g_object_new (GSM_TYPE_APP_DIALOG,
+ "name", name,
+ "command", command,
+ "comment", comment,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
+
+gboolean
+gsm_app_dialog_run (GsmAppDialog *dialog,
+ char **name_p,
+ char **command_p,
+ char **comment_p)
+{
+ gboolean retval;
+
+ retval = FALSE;
+
+ while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+ const char *name;
+ const char *exec;
+ const char *comment;
+ const char *error_msg;
+ GError *error;
+ char **argv;
+ int argc;
+ gboolean changed;
+
+ name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dialog));
+ exec = gsm_app_dialog_get_command (GSM_APP_DIALOG (dialog));
+ comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dialog));
+
+ error = NULL;
+ error_msg = NULL;
+
+ if (gsm_util_text_is_blank (exec)) {
+ error_msg = _("The startup command cannot be empty");
+ } else {
+ if (!g_shell_parse_argv (exec, &argc, &argv, &error)) {
+ if (error != NULL) {
+ error_msg = error->message;
+ } else {
+ error_msg = _("The startup command is not valid");
+ }
+ }
+ }
+
+ if (error_msg != NULL) {
+ GtkWidget *msgbox;
+
+ msgbox = gtk_message_dialog_new (GTK_WINDOW (dialog),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s", error_msg);
+
+ if (error != NULL) {
+ g_error_free (error);
+ }
+
+ gtk_dialog_run (GTK_DIALOG (msgbox));
+
+ gtk_widget_destroy (msgbox);
+
+ continue;
+ }
+
+ changed = FALSE;
+
+ if (gsm_util_text_is_blank (name)) {
+ name = argv[0];
+ }
+
+ if (name_p) {
+ *name_p = g_strdup (name);
+ }
+
+ g_strfreev (argv);
+
+ if (command_p) {
+ *command_p = g_strdup (exec);
+ }
+
+ if (comment_p) {
+ *comment_p = g_strdup (comment);
+ }
+
+ retval = TRUE;
+ break;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return retval;
+}
diff --git a/capplet/gsm-app-dialog.h b/capplet/gsm-app-dialog.h
new file mode 100644
index 0000000..2df5056
--- /dev/null
+++ b/capplet/gsm-app-dialog.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GSM_APP_DIALOG_H
+#define __GSM_APP_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GSM_TYPE_APP_DIALOG (gsm_app_dialog_get_type ())
+#define GSM_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_APP_DIALOG, GsmAppDialog))
+#define GSM_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
+#define GSM_IS_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_APP_DIALOG))
+#define GSM_IS_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_APP_DIALOG))
+#define GSM_APP_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogClass))
+
+typedef struct GsmAppDialogPrivate GsmAppDialogPrivate;
+
+typedef struct
+{
+ GtkDialog parent;
+ GsmAppDialogPrivate *priv;
+} GsmAppDialog;
+
+typedef struct
+{
+ GtkDialogClass parent_class;
+} GsmAppDialogClass;
+
+GType gsm_app_dialog_get_type (void);
+
+GtkWidget * gsm_app_dialog_new (const char *name,
+ const char *command,
+ const char *comment);
+
+gboolean gsm_app_dialog_run (GsmAppDialog *dialog,
+ char **name_p,
+ char **command_p,
+ char **comment_p);
+
+const char * gsm_app_dialog_get_name (GsmAppDialog *dialog);
+const char * gsm_app_dialog_get_command (GsmAppDialog *dialog);
+const char * gsm_app_dialog_get_comment (GsmAppDialog *dialog);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GSM_APP_DIALOG_H */
diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c
new file mode 100644
index 0000000..b3a8e74
--- /dev/null
+++ b/capplet/gsm-properties-dialog.c
@@ -0,0 +1,819 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <mateconf/mateconf-client.h>
+
+#include "gsm-properties-dialog.h"
+#include "gsm-app-dialog.h"
+#include "eggdesktopfile.h"
+#include "gsm-util.h"
+#include "gsp-app.h"
+#include "gsp-app-manager.h"
+
+#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate))
+
+#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
+
+#define REALLY_IDENTICAL_STRING(a, b) \
+ ((a && b && !strcmp (a, b)) || (!a && !b))
+
+#define GTKBUILDER_FILE "session-properties.ui"
+
+#define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview"
+#define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button"
+#define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button"
+#define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button"
+#define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button"
+#define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle"
+
+#define STARTUP_APP_ICON "system-run"
+
+#define SPC_MATECONF_CONFIG_PREFIX "/apps/mate-session/options"
+#define SPC_MATECONF_AUTOSAVE_KEY SPC_MATECONF_CONFIG_PREFIX "/auto_save_session"
+
+struct GsmPropertiesDialogPrivate
+{
+ GtkBuilder *xml;
+ GtkListStore *list_store;
+ GtkTreeModel *tree_filter;
+
+ GtkTreeView *treeview;
+ GtkWidget *add_button;
+ GtkWidget *delete_button;
+ GtkWidget *edit_button;
+
+ GtkWidget *remember_toggle;
+
+ GspAppManager *manager;
+};
+
+enum {
+ STORE_COL_VISIBLE = 0,
+ STORE_COL_ENABLED,
+ STORE_COL_GICON,
+ STORE_COL_DESCRIPTION,
+ STORE_COL_APP,
+ NUMBER_OF_COLUMNS
+};
+
+static void gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass);
+static void gsm_properties_dialog_init (GsmPropertiesDialog *properties_dialog);
+static void gsm_properties_dialog_finalize (GObject *object);
+
+G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG)
+
+static gboolean
+find_by_app (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GspApp *app)
+{
+ GspApp *iter_app = NULL;
+
+ if (!gtk_tree_model_get_iter_first (model, iter)) {
+ return FALSE;
+ }
+
+ do {
+ gtk_tree_model_get (model, iter,
+ STORE_COL_APP, &iter_app,
+ -1);
+
+ if (iter_app == app) {
+ g_object_unref (iter_app);
+ return TRUE;
+ }
+ } while (gtk_tree_model_iter_next (model, iter));
+
+ return FALSE;
+}
+
+static void
+_fill_iter_from_app (GtkListStore *list_store,
+ GtkTreeIter *iter,
+ GspApp *app)
+{
+ gboolean hidden;
+ gboolean enabled;
+ GIcon *icon;
+ const char *description;
+
+ hidden = gsp_app_get_hidden (app);
+ enabled = gsp_app_get_enabled (app);
+ icon = gsp_app_get_icon (app);
+ description = gsp_app_get_description (app);
+
+ if (G_IS_THEMED_ICON (icon)) {
+ GtkIconTheme *theme;
+ const char * const *icon_names;
+
+ theme = gtk_icon_theme_get_default ();
+ icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon));
+ if (icon_names[0] == NULL ||
+ !gtk_icon_theme_has_icon (theme, icon_names[0])) {
+ g_object_unref (icon);
+ icon = NULL;
+ }
+ } else if (G_IS_FILE_ICON (icon)) {
+ GFile *iconfile;
+
+ iconfile = g_file_icon_get_file (G_FILE_ICON (icon));
+ if (!g_file_query_exists (iconfile, NULL)) {
+ g_object_unref (icon);
+ icon = NULL;
+ }
+ }
+
+ if (icon == NULL) {
+ icon = g_themed_icon_new (STARTUP_APP_ICON);
+ }
+
+ gtk_list_store_set (list_store, iter,
+ STORE_COL_VISIBLE, !hidden,
+ STORE_COL_ENABLED, enabled,
+ STORE_COL_GICON, icon,
+ STORE_COL_DESCRIPTION, description,
+ STORE_COL_APP, app,
+ -1);
+ g_object_unref (icon);
+}
+
+static void
+_app_changed (GsmPropertiesDialog *dialog,
+ GspApp *app)
+{
+ GtkTreeIter iter;
+
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+ &iter, app)) {
+ return;
+ }
+
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
+}
+
+static void
+append_app (GsmPropertiesDialog *dialog,
+ GspApp *app)
+{
+ GtkTreeIter iter;
+
+ gtk_list_store_append (dialog->priv->list_store, &iter);
+ _fill_iter_from_app (dialog->priv->list_store, &iter, app);
+
+ g_signal_connect_swapped (app, "changed",
+ G_CALLBACK (_app_changed), dialog);
+}
+
+static void
+_app_added (GsmPropertiesDialog *dialog,
+ GspApp *app,
+ GspAppManager *manager)
+{
+ append_app (dialog, app);
+}
+
+static void
+_app_removed (GsmPropertiesDialog *dialog,
+ GspApp *app,
+ GspAppManager *manager)
+{
+ GtkTreeIter iter;
+
+ if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store),
+ &iter, app)) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (app,
+ _app_changed,
+ dialog);
+ gtk_list_store_remove (dialog->priv->list_store, &iter);
+}
+
+static void
+populate_model (GsmPropertiesDialog *dialog)
+{
+ GSList *apps;
+ GSList *l;
+
+ apps = gsp_app_manager_get_apps (dialog->priv->manager);
+ for (l = apps; l != NULL; l = l->next) {
+ append_app (dialog, GSP_APP (l->data));
+ }
+ g_slist_free (apps);
+}
+
+static void
+on_selection_changed (GtkTreeSelection *selection,
+ GsmPropertiesDialog *dialog)
+{
+ gboolean sel;
+
+ sel = gtk_tree_selection_get_selected (selection, NULL, NULL);
+
+ gtk_widget_set_sensitive (dialog->priv->edit_button, sel);
+ gtk_widget_set_sensitive (dialog->priv->delete_button, sel);
+}
+
+static void
+on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer,
+ char *path,
+ GsmPropertiesDialog *dialog)
+{
+ GtkTreeIter iter;
+ GspApp *app;
+ gboolean active;
+
+ if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter, path)) {
+ return;
+ }
+
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
+ -1);
+
+ active = gtk_cell_renderer_toggle_get_active (cell_renderer);
+ active = !active;
+
+ if (app) {
+ gsp_app_set_enabled (app, active);
+ g_object_unref (app);
+ }
+}
+
+static void
+on_drag_data_received (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ GsmPropertiesDialog *dialog)
+{
+ gboolean dnd_success;
+
+ dnd_success = FALSE;
+
+ if (data != NULL) {
+ char **filenames;
+ int i;
+
+ filenames = gtk_selection_data_get_uris (data);
+
+ for (i = 0; filenames[i] && filenames[i][0]; i++) {
+ /* Return success if at least one file succeeded */
+ gboolean file_success;
+ file_success = gsp_app_copy_desktop_file (filenames[i]);
+ dnd_success = dnd_success || file_success;
+ }
+
+ g_strfreev (filenames);
+ }
+
+ gtk_drag_finish (drag_context, dnd_success, FALSE, time);
+ g_signal_stop_emission_by_name (widget, "drag_data_received");
+}
+
+static void
+on_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ GsmPropertiesDialog *dialog)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GspApp *app;
+
+ gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
+ -1);
+
+ if (app) {
+ g_object_set_data_full (G_OBJECT (context), "gsp-app",
+ g_object_ref (app), g_object_unref);
+ g_object_unref (app);
+ }
+
+}
+
+static void
+on_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ GsmPropertiesDialog *dialog)
+{
+ GspApp *app;
+
+ app = g_object_get_data (G_OBJECT (context), "gsp-app");
+ if (app) {
+ const char *uris[2];
+ char *uri;
+
+ uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL);
+
+ uris[0] = uri;
+ uris[1] = NULL;
+ gtk_selection_data_set_uris (selection_data, (char **) uris);
+
+ g_free (uri);
+ }
+}
+
+static void
+on_add_app_clicked (GtkWidget *widget,
+ GsmPropertiesDialog *dialog)
+{
+ GtkWidget *add_dialog;
+ char *name;
+ char *exec;
+ char *comment;
+
+ add_dialog = gsm_app_dialog_new (NULL, NULL, NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (add_dialog),
+ GTK_WINDOW (dialog));
+
+ if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog),
+ &name, &exec, &comment)) {
+ gsp_app_create (name, comment, exec);
+ g_free (name);
+ g_free (exec);
+ g_free (comment);
+ }
+}
+
+static void
+on_delete_app_clicked (GtkWidget *widget,
+ GsmPropertiesDialog *dialog)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GspApp *app;
+
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ return;
+ }
+
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
+ -1);
+
+ if (app) {
+ gsp_app_delete (app);
+ g_object_unref (app);
+ }
+}
+
+static void
+on_edit_app_clicked (GtkWidget *widget,
+ GsmPropertiesDialog *dialog)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GspApp *app;
+
+ selection = gtk_tree_view_get_selection (dialog->priv->treeview);
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ return;
+ }
+
+ app = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter),
+ &iter,
+ STORE_COL_APP, &app,
+ -1);
+
+ if (app) {
+ GtkWidget *edit_dialog;
+ char *name;
+ char *exec;
+ char *comment;
+
+ edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app),
+ gsp_app_get_exec (app),
+ gsp_app_get_comment (app));
+ gtk_window_set_transient_for (GTK_WINDOW (edit_dialog),
+ GTK_WINDOW (dialog));
+
+ if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog),
+ &name, &exec, &comment)) {
+ gsp_app_update (app, name, comment, exec);
+ g_free (name);
+ g_free (exec);
+ g_free (comment);
+ }
+
+ g_object_unref (app);
+ }
+}
+
+static void
+on_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GsmPropertiesDialog *dialog)
+{
+ on_edit_app_clicked (NULL, dialog);
+}
+
+static void
+on_autosave_value_notify (MateConfClient *client,
+ guint id,
+ MateConfEntry *entry,
+ GsmPropertiesDialog *dialog)
+{
+ gboolean gval;
+ gboolean bval;
+
+ gval = mateconf_value_get_bool (entry->value);
+ bval = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->remember_toggle));
+
+ if (bval != gval) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->remember_toggle), gval);
+ }
+}
+
+static void
+on_autosave_value_toggled (GtkToggleButton *button,
+ GsmPropertiesDialog *dialog)
+{
+ MateConfClient *client;
+ gboolean gval;
+ gboolean bval;
+
+ client = mateconf_client_get_default ();
+ gval = mateconf_client_get_bool (client, SPC_MATECONF_AUTOSAVE_KEY, NULL);
+ bval = gtk_toggle_button_get_active (button);
+
+ if (gval != bval) {
+ mateconf_client_set_bool (client, SPC_MATECONF_AUTOSAVE_KEY, bval, NULL);
+ }
+ g_object_unref (client);
+}
+
+static void
+on_save_session_clicked (GtkWidget *widget,
+ GsmPropertiesDialog *dialog)
+{
+ g_debug ("Session saving is not implemented yet!");
+}
+
+static void
+setup_dialog (GsmPropertiesDialog *dialog)
+{
+ GtkTreeView *treeview;
+ GtkWidget *button;
+ GtkTreeModel *tree_filter;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+ MateConfClient *client;
+ GtkTargetList *targetlist;
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+
+ dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_ICON,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT);
+ tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store),
+ NULL);
+ g_object_unref (dialog->priv->list_store);
+ dialog->priv->tree_filter = tree_filter;
+
+ gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter),
+ STORE_COL_VISIBLE);
+
+ treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_TREEVIEW_WIDGET_NAME));
+ dialog->priv->treeview = treeview;
+
+ gtk_tree_view_set_model (treeview, tree_filter);
+ g_object_unref (tree_filter);
+
+ gtk_tree_view_set_headers_visible (treeview, FALSE);
+ g_signal_connect (treeview,
+ "row-activated",
+ G_CALLBACK (on_row_activated),
+ dialog);
+
+ selection = gtk_tree_view_get_selection (treeview);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (selection,
+ "changed",
+ G_CALLBACK (on_selection_changed),
+ dialog);
+
+ /* CHECKBOX COLUMN */
+ renderer = gtk_cell_renderer_toggle_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Enabled"),
+ renderer,
+ "active", STORE_COL_ENABLED,
+ NULL);
+ gtk_tree_view_append_column (treeview, column);
+ g_signal_connect (renderer,
+ "toggled",
+ G_CALLBACK (on_startup_enabled_toggled),
+ dialog);
+
+ /* ICON COLUMN */
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Icon"),
+ renderer,
+ "gicon", STORE_COL_GICON,
+ "sensitive", STORE_COL_ENABLED,
+ NULL);
+ g_object_set (renderer,
+ "stock-size", GSM_PROPERTIES_ICON_SIZE,
+ NULL);
+ gtk_tree_view_append_column (treeview, column);
+
+ /* NAME COLUMN */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Program"),
+ renderer,
+ "markup", STORE_COL_DESCRIPTION,
+ "sensitive", STORE_COL_ENABLED,
+ NULL);
+ g_object_set (renderer,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ gtk_tree_view_append_column (treeview, column);
+
+
+ gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION);
+ gtk_tree_view_set_search_column (treeview, STORE_COL_DESCRIPTION);
+ gtk_tree_view_set_rules_hint (treeview, TRUE);
+
+ gtk_tree_view_enable_model_drag_source (treeview,
+ GDK_BUTTON1_MASK|GDK_BUTTON2_MASK,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview));
+
+ gtk_drag_dest_set (GTK_WIDGET (treeview),
+ GTK_DEST_DEFAULT_ALL,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview));
+
+ /* we don't want to accept drags coming from this widget */
+ targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview));
+ if (targetlist != NULL) {
+ GList *list;
+ list = targetlist->list;
+ while (list != NULL) {
+ GtkTargetPair *targetpair;
+ targetpair = list->data;
+ targetpair->flags = GTK_TARGET_OTHER_WIDGET;
+ list = list->next;
+ }
+ }
+
+ g_signal_connect (treeview, "drag_begin",
+ G_CALLBACK (on_drag_begin),
+ dialog);
+ g_signal_connect (treeview, "drag_data_get",
+ G_CALLBACK (on_drag_data_get),
+ dialog);
+ g_signal_connect (treeview, "drag_data_received",
+ G_CALLBACK (on_drag_data_received),
+ dialog);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store),
+ STORE_COL_DESCRIPTION,
+ GTK_SORT_ASCENDING);
+
+
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_ADD_WIDGET_NAME));
+ dialog->priv->add_button = button;
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK (on_add_app_clicked),
+ dialog);
+
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_DELETE_WIDGET_NAME));
+ dialog->priv->delete_button = button;
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK (on_delete_app_clicked),
+ dialog);
+
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_EDIT_WIDGET_NAME));
+ dialog->priv->edit_button = button;
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK (on_edit_app_clicked),
+ dialog);
+
+ client = mateconf_client_get_default ();
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_REMEMBER_WIDGET_NAME));
+ dialog->priv->remember_toggle = button;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ mateconf_client_get_bool (client, SPC_MATECONF_AUTOSAVE_KEY, NULL));
+ mateconf_client_notify_add (client,
+ SPC_MATECONF_AUTOSAVE_KEY,
+ (MateConfClientNotifyFunc)on_autosave_value_notify,
+ dialog,
+ NULL,
+ NULL);
+ g_object_unref (client);
+
+ g_signal_connect (button,
+ "toggled",
+ G_CALLBACK (on_autosave_value_toggled),
+ dialog);
+
+ button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ CAPPLET_SAVE_WIDGET_NAME));
+ g_signal_connect (button,
+ "clicked",
+ G_CALLBACK (on_save_session_clicked),
+ dialog);
+
+ dialog->priv->manager = gsp_app_manager_get ();
+ gsp_app_manager_fill (dialog->priv->manager);
+ g_signal_connect_swapped (dialog->priv->manager, "added",
+ G_CALLBACK (_app_added), dialog);
+ g_signal_connect_swapped (dialog->priv->manager, "removed",
+ G_CALLBACK (_app_removed), dialog);
+
+ populate_model (dialog);
+}
+
+static GObject *
+gsm_properties_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GsmPropertiesDialog *dialog;
+
+ dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ setup_dialog (dialog);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ return G_OBJECT (dialog);
+}
+
+static void
+gsm_properties_dialog_dispose (GObject *object)
+{
+ GsmPropertiesDialog *dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
+
+ dialog = GSM_PROPERTIES_DIALOG (object);
+
+ if (dialog->priv->xml != NULL) {
+ g_object_unref (dialog->priv->xml);
+ dialog->priv->xml = NULL;
+ }
+
+ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object);
+
+ /* it's important to do this after chaining to the parent dispose
+ * method because we want to make sure the treeview has been disposed
+ * and removed all its references to GspApp objects */
+ if (dialog->priv->manager != NULL) {
+ g_object_unref (dialog->priv->manager);
+ dialog->priv->manager = NULL;
+ }
+}
+
+static void
+gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = gsm_properties_dialog_constructor;
+ object_class->dispose = gsm_properties_dialog_dispose;
+ object_class->finalize = gsm_properties_dialog_finalize;
+
+ g_type_class_add_private (klass, sizeof (GsmPropertiesDialogPrivate));
+}
+
+static void
+gsm_properties_dialog_init (GsmPropertiesDialog *dialog)
+{
+ GtkWidget *content_area;
+ GtkWidget *widget;
+ MateConfClient *client;
+ GError *error;
+
+ dialog->priv = GSM_PROPERTIES_DIALOG_GET_PRIVATE (dialog);
+
+ client = mateconf_client_get_default ();
+ mateconf_client_add_dir (client,
+ SPC_MATECONF_CONFIG_PREFIX,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+
+ dialog->priv->xml = gtk_builder_new ();
+ gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE);
+
+ error = NULL;
+ if (!gtk_builder_add_from_file (dialog->priv->xml,
+ GTKBUILDER_DIR "/" GTKBUILDER_FILE,
+ &error)) {
+ if (error) {
+ g_warning ("Could not load capplet UI file: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Could not load capplet UI file.");
+ }
+ }
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml,
+ "main-notebook"));
+ gtk_container_add (GTK_CONTAINER (content_area), widget);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+ gtk_box_set_spacing (GTK_BOX (content_area), 2);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "mate-session-properties");
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences"));
+}
+
+static void
+gsm_properties_dialog_finalize (GObject *object)
+{
+ GsmPropertiesDialog *dialog;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object));
+
+ dialog = GSM_PROPERTIES_DIALOG (object);
+
+ g_return_if_fail (dialog->priv != NULL);
+
+ G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gsm_properties_dialog_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG,
+ NULL);
+
+ return GTK_WIDGET (object);
+}
diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h
new file mode 100644
index 0000000..cbea612
--- /dev/null
+++ b/capplet/gsm-properties-dialog.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GSM_PROPERTIES_DIALOG_H
+#define __GSM_PROPERTIES_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GSM_TYPE_PROPERTIES_DIALOG (gsm_properties_dialog_get_type ())
+#define GSM_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialog))
+#define GSM_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
+#define GSM_IS_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_PROPERTIES_DIALOG))
+#define GSM_IS_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_PROPERTIES_DIALOG))
+#define GSM_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass))
+
+typedef struct GsmPropertiesDialogPrivate GsmPropertiesDialogPrivate;
+
+typedef struct
+{
+ GtkDialog parent;
+ GsmPropertiesDialogPrivate *priv;
+} GsmPropertiesDialog;
+
+typedef struct
+{
+ GtkDialogClass parent_class;
+} GsmPropertiesDialogClass;
+
+GType gsm_properties_dialog_get_type (void);
+
+GtkWidget * gsm_properties_dialog_new (void);
+
+#define GSM_PROPERTIES_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GSM_PROPERTIES_DIALOG_H */
diff --git a/capplet/gsp-app-manager.c b/capplet/gsp-app-manager.c
new file mode 100644
index 0000000..bcd6d40
--- /dev/null
+++ b/capplet/gsp-app-manager.c
@@ -0,0 +1,593 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <string.h>
+
+#include "gsm-util.h"
+#include "gsp-app.h"
+
+#include "gsp-app-manager.h"
+
+static GspAppManager *manager = NULL;
+
+typedef struct {
+ char *dir;
+ int index;
+ GFileMonitor *monitor;
+} GspXdgDir;
+
+struct _GspAppManagerPrivate {
+ GSList *apps;
+ GSList *dirs;
+};
+
+#define GSP_APP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP_MANAGER, GspAppManagerPrivate))
+
+
+enum {
+ ADDED,
+ REMOVED,
+ LAST_SIGNAL
+};
+
+static guint gsp_app_manager_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GspAppManager, gsp_app_manager, G_TYPE_OBJECT)
+
+static void gsp_app_manager_dispose (GObject *object);
+static void gsp_app_manager_finalize (GObject *object);
+static void _gsp_app_manager_app_unref (GspApp *app,
+ GspAppManager *manager);
+static void _gsp_app_manager_app_removed (GspAppManager *manager,
+ GspApp *app);
+
+static GspXdgDir *
+_gsp_xdg_dir_new (const char *dir,
+ int index)
+{
+ GspXdgDir *xdgdir;
+
+ xdgdir = g_slice_new (GspXdgDir);
+
+ xdgdir->dir = g_strdup (dir);
+ xdgdir->index = index;
+ xdgdir->monitor = NULL;
+
+ return xdgdir;
+}
+
+static void
+_gsp_xdg_dir_free (GspXdgDir *xdgdir)
+{
+ if (xdgdir->dir) {
+ g_free (xdgdir->dir);
+ xdgdir->dir = NULL;
+ }
+
+ if (xdgdir->monitor) {
+ g_file_monitor_cancel (xdgdir->monitor);
+ g_object_unref (xdgdir->monitor);
+ xdgdir->monitor = NULL;
+ }
+
+ g_slice_free (GspXdgDir, xdgdir);
+}
+
+static void
+gsp_app_manager_class_init (GspAppManagerClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->dispose = gsp_app_manager_dispose;
+ gobject_class->finalize = gsp_app_manager_finalize;
+
+ gsp_app_manager_signals[ADDED] =
+ g_signal_new ("added",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GspAppManagerClass,
+ added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+ gsp_app_manager_signals[REMOVED] =
+ g_signal_new ("removed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GspAppManagerClass,
+ removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+ g_type_class_add_private (class, sizeof (GspAppManagerPrivate));
+}
+
+static void
+gsp_app_manager_init (GspAppManager *manager)
+{
+ manager->priv = GSP_APP_MANAGER_GET_PRIVATE (manager);
+
+ memset (manager->priv, 0, sizeof (GspAppManagerPrivate));
+}
+
+static void
+gsp_app_manager_dispose (GObject *object)
+{
+ GspAppManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSP_IS_APP_MANAGER (object));
+
+ manager = GSP_APP_MANAGER (object);
+
+ /* we unref GspApp objects in dispose since they might need to
+ * reference us during their dispose/finalize */
+ g_slist_foreach (manager->priv->apps,
+ (GFunc) _gsp_app_manager_app_unref, manager);
+ g_slist_free (manager->priv->apps);
+ manager->priv->apps = NULL;
+
+ G_OBJECT_CLASS (gsp_app_manager_parent_class)->dispose (object);
+}
+
+static void
+gsp_app_manager_finalize (GObject *object)
+{
+ GspAppManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSP_IS_APP_MANAGER (object));
+
+ manager = GSP_APP_MANAGER (object);
+
+ g_slist_foreach (manager->priv->dirs,
+ (GFunc) _gsp_xdg_dir_free, NULL);
+ g_slist_free (manager->priv->dirs);
+ manager->priv->dirs = NULL;
+
+ G_OBJECT_CLASS (gsp_app_manager_parent_class)->finalize (object);
+
+ manager = NULL;
+}
+
+static void
+_gsp_app_manager_emit_added (GspAppManager *manager,
+ GspApp *app)
+{
+ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[ADDED],
+ 0, app);
+}
+
+static void
+_gsp_app_manager_emit_removed (GspAppManager *manager,
+ GspApp *app)
+{
+ g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[REMOVED],
+ 0, app);
+}
+
+/*
+ * Directories
+ */
+
+static int
+gsp_app_manager_get_dir_index (GspAppManager *manager,
+ const char *dir)
+{
+ GSList *l;
+ GspXdgDir *xdgdir;
+
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), -1);
+ g_return_val_if_fail (dir != NULL, -1);
+
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
+ xdgdir = l->data;
+ if (strcmp (dir, xdgdir->dir) == 0) {
+ return xdgdir->index;
+ }
+ }
+
+ return -1;
+}
+
+const char *
+gsp_app_manager_get_dir (GspAppManager *manager,
+ unsigned int index)
+{
+ GSList *l;
+ GspXdgDir *xdgdir;
+
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
+ xdgdir = l->data;
+ if (index == xdgdir->index) {
+ return xdgdir->dir;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+_gsp_app_manager_find_dir_with_basename (GspAppManager *manager,
+ const char *basename,
+ int minimum_index)
+{
+ GSList *l;
+ GspXdgDir *xdgdir;
+ char *path;
+ GKeyFile *keyfile;
+ int result = -1;
+
+ path = NULL;
+ keyfile = g_key_file_new ();
+
+ for (l = manager->priv->dirs; l != NULL; l = l->next) {
+ xdgdir = l->data;
+
+ if (xdgdir->index <= minimum_index) {
+ continue;
+ }
+
+ g_free (path);
+ path = g_build_filename (xdgdir->dir, basename, NULL);
+ if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+ continue;
+ }
+
+ if (!g_key_file_load_from_file (keyfile, path,
+ G_KEY_FILE_NONE, NULL)) {
+ continue;
+ }
+
+ /* the file exists and is readable */
+ if (result == -1) {
+ result = xdgdir->index;
+ } else {
+ result = MIN (result, xdgdir->index);
+ }
+ }
+
+ g_key_file_free (keyfile);
+ g_free (path);
+
+ return result;
+}
+
+static void
+_gsp_app_manager_handle_delete (GspAppManager *manager,
+ GspApp *app,
+ const char *basename,
+ int index)
+{
+ unsigned int position;
+ unsigned int system_position;
+
+ position = gsp_app_get_xdg_position (app);
+ system_position = gsp_app_get_xdg_system_position (app);
+
+ if (system_position < index) {
+ /* it got deleted, but we don't even care about it */
+ return;
+ }
+
+ if (index < position) {
+ /* it got deleted, but in a position earlier than the current
+ * one. This happens when the user file was changed and became
+ * identical to the system file; in this case, the user file is
+ * simply removed. */
+ g_assert (index == 0);
+ return;
+ }
+
+ if (position == index &&
+ (system_position == index || system_position == G_MAXUINT)) {
+ /* the file used by the user was deleted, and there's no other
+ * file in system directories. So it really got deleted. */
+ _gsp_app_manager_app_removed (manager, app);
+ return;
+ }
+
+ if (system_position == index) {
+ /* then we know that position != index; we just hae to tell
+ * GspApp if there's still a system directory containing this
+ * basename */
+ int new_system;
+
+ new_system = _gsp_app_manager_find_dir_with_basename (manager,
+ basename,
+ index);
+ if (new_system < 0) {
+ gsp_app_set_xdg_system_position (app, G_MAXUINT);
+ } else {
+ gsp_app_set_xdg_system_position (app, new_system);
+ }
+
+ return;
+ }
+
+ if (position == index) {
+ /* then we know that system_position != G_MAXUINT; we need to
+ * tell GspApp to change position to system_position */
+ const char *dir;
+
+ dir = gsp_app_manager_get_dir (manager, system_position);
+ if (dir) {
+ char *path;
+
+ path = g_build_filename (dir, basename, NULL);
+ gsp_app_reload_at (app, path,
+ (unsigned int) system_position);
+ g_free (path);
+ } else {
+ _gsp_app_manager_app_removed (manager, app);
+ }
+
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+gsp_app_manager_xdg_dir_monitor (GFileMonitor *monitor,
+ GFile *child,
+ GFile *other_file,
+ GFileMonitorEvent flags,
+ gpointer data)
+{
+ GspAppManager *manager;
+ GspApp *old_app;
+ GspApp *app;
+ GFile *parent;
+ char *basename;
+ char *dir;
+ char *path;
+ int index;
+
+ manager = GSP_APP_MANAGER (data);
+
+ basename = g_file_get_basename (child);
+ if (!g_str_has_suffix (basename, ".desktop")) {
+ /* not a desktop file, we can ignore */
+ g_free (basename);
+ return TRUE;
+ }
+ old_app = gsp_app_manager_find_app_with_basename (manager, basename);
+
+ parent = g_file_get_parent (child);
+ dir = g_file_get_path (parent);
+ g_object_unref (parent);
+
+ index = gsp_app_manager_get_dir_index (manager, dir);
+ if (index < 0) {
+ /* not a directory we know; should never happen, though */
+ g_free (dir);
+ return TRUE;
+ }
+
+ path = g_file_get_path (child);
+
+ switch (flags) {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ case G_FILE_MONITOR_EVENT_CREATED:
+ /* we just do as if it was a new file: GspApp is clever enough
+ * to do the right thing */
+ app = gsp_app_new (path, (unsigned int) index);
+
+ /* we didn't have this app before, so add it */
+ if (old_app == NULL && app != NULL) {
+ gsp_app_manager_add (manager, app);
+ g_object_unref (app);
+ }
+ /* else: it was just updated, GspApp took care of
+ * sending the event */
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ if (!old_app) {
+ /* it got deleted, but we don't know about it, so
+ * nothing to do */
+ break;
+ }
+
+ _gsp_app_manager_handle_delete (manager, old_app,
+ basename, index);
+ break;
+ default:
+ break;
+ }
+
+ g_free (path);
+ g_free (dir);
+ g_free (basename);
+
+ return TRUE;
+}
+
+/*
+ * Initialization
+ */
+
+static void
+_gsp_app_manager_fill_from_dir (GspAppManager *manager,
+ GspXdgDir *xdgdir)
+{
+ GFile *file;
+ GDir *dir;
+ const char *name;
+
+ file = g_file_new_for_path (xdgdir->dir);
+ xdgdir->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
+ NULL, NULL);
+ g_object_unref (file);
+
+ if (xdgdir->monitor) {
+ g_signal_connect (xdgdir->monitor, "changed",
+ G_CALLBACK (gsp_app_manager_xdg_dir_monitor),
+ manager);
+ }
+
+ dir = g_dir_open (xdgdir->dir, 0, NULL);
+ if (!dir) {
+ return;
+ }
+
+ while ((name = g_dir_read_name (dir))) {
+ GspApp *app;
+ char *desktop_file_path;
+
+ if (!g_str_has_suffix (name, ".desktop")) {
+ continue;
+ }
+
+ desktop_file_path = g_build_filename (xdgdir->dir, name, NULL);
+ app = gsp_app_new (desktop_file_path, xdgdir->index);
+
+ if (app != NULL) {
+ gsp_app_manager_add (manager, app);
+ g_object_unref (app);
+ }
+
+ g_free (desktop_file_path);
+ }
+
+ g_dir_close (dir);
+}
+
+void
+gsp_app_manager_fill (GspAppManager *manager)
+{
+ char **autostart_dirs;
+ int i;
+
+ if (manager->priv->apps != NULL)
+ return;
+
+ autostart_dirs = gsm_util_get_autostart_dirs ();
+ /* we always assume that the first directory is the user one */
+ g_assert (g_str_has_prefix (autostart_dirs[0],
+ g_get_user_config_dir ()));
+
+ for (i = 0; autostart_dirs[i] != NULL; i++) {
+ GspXdgDir *xdgdir;
+
+ if (gsp_app_manager_get_dir_index (manager,
+ autostart_dirs[i]) >= 0) {
+ continue;
+ }
+
+ xdgdir = _gsp_xdg_dir_new (autostart_dirs[i], i);
+ manager->priv->dirs = g_slist_prepend (manager->priv->dirs,
+ xdgdir);
+
+ _gsp_app_manager_fill_from_dir (manager, xdgdir);
+ }
+
+ g_strfreev (autostart_dirs);
+}
+
+/*
+ * App handling
+ */
+
+static void
+_gsp_app_manager_app_unref (GspApp *app,
+ GspAppManager *manager)
+{
+ g_signal_handlers_disconnect_by_func (app,
+ _gsp_app_manager_app_removed,
+ manager);
+ g_object_unref (app);
+}
+
+static void
+_gsp_app_manager_app_removed (GspAppManager *manager,
+ GspApp *app)
+{
+ _gsp_app_manager_emit_removed (manager, app);
+ manager->priv->apps = g_slist_remove (manager->priv->apps, app);
+ _gsp_app_manager_app_unref (app, manager);
+}
+
+void
+gsp_app_manager_add (GspAppManager *manager,
+ GspApp *app)
+{
+ g_return_if_fail (GSP_IS_APP_MANAGER (manager));
+ g_return_if_fail (GSP_IS_APP (app));
+
+ manager->priv->apps = g_slist_prepend (manager->priv->apps,
+ g_object_ref (app));
+ g_signal_connect_swapped (app, "removed",
+ G_CALLBACK (_gsp_app_manager_app_removed),
+ manager);
+ _gsp_app_manager_emit_added (manager, app);
+}
+
+GspApp *
+gsp_app_manager_find_app_with_basename (GspAppManager *manager,
+ const char *basename)
+{
+ GSList *l;
+ GspApp *app;
+
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+ g_return_val_if_fail (basename != NULL, NULL);
+
+ for (l = manager->priv->apps; l != NULL; l = l->next) {
+ app = GSP_APP (l->data);
+ if (strcmp (basename, gsp_app_get_basename (app)) == 0)
+ return app;
+ }
+
+ return NULL;
+}
+
+/*
+ * Singleton
+ */
+
+GspAppManager *
+gsp_app_manager_get (void)
+{
+ if (manager == NULL) {
+ manager = g_object_new (GSP_TYPE_APP_MANAGER, NULL);
+ return manager;
+ } else {
+ return g_object_ref (manager);
+ }
+}
+
+GSList *
+gsp_app_manager_get_apps (GspAppManager *manager)
+{
+ g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL);
+
+ return g_slist_copy (manager->priv->apps);
+}
diff --git a/capplet/gsp-app-manager.h b/capplet/gsp-app-manager.h
new file mode 100644
index 0000000..7cac3eb
--- /dev/null
+++ b/capplet/gsp-app-manager.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GSP_APP_MANAGER_H
+#define __GSP_APP_MANAGER_H
+
+#include <glib-object.h>
+
+#include <gsp-app.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GSP_TYPE_APP_MANAGER (gsp_app_manager_get_type ())
+#define GSP_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP_MANAGER, GspAppManager))
+#define GSP_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
+#define GSP_IS_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP_MANAGER))
+#define GSP_IS_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP_MANAGER))
+#define GSP_APP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP_MANAGER, GspAppManagerClass))
+
+typedef struct _GspAppManager GspAppManager;
+typedef struct _GspAppManagerClass GspAppManagerClass;
+
+typedef struct _GspAppManagerPrivate GspAppManagerPrivate;
+
+struct _GspAppManagerClass
+{
+ GObjectClass parent_class;
+
+ void (* added) (GspAppManager *manager,
+ GspApp *app);
+ void (* removed) (GspAppManager *manager,
+ GspApp *app);
+};
+
+struct _GspAppManager
+{
+ GObject parent_instance;
+
+ GspAppManagerPrivate *priv;
+};
+
+GType gsp_app_manager_get_type (void);
+
+GspAppManager *gsp_app_manager_get (void);
+
+void gsp_app_manager_fill (GspAppManager *manager);
+
+GSList *gsp_app_manager_get_apps (GspAppManager *manager);
+
+GspApp *gsp_app_manager_find_app_with_basename (GspAppManager *manager,
+ const char *basename);
+
+const char *gsp_app_manager_get_dir (GspAppManager *manager,
+ unsigned int index);
+
+void gsp_app_manager_add (GspAppManager *manager,
+ GspApp *app);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GSP_APP_MANAGER_H */
diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c
new file mode 100644
index 0000000..fe85d80
--- /dev/null
+++ b/capplet/gsp-app.c
@@ -0,0 +1,1050 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gsm-app-dialog.h"
+#include "gsm-properties-dialog.h"
+#include "gsm-util.h"
+#include "gsp-app-manager.h"
+#include "gsp-keyfile.h"
+
+#include "gsp-app.h"
+
+#define GSP_APP_SAVE_DELAY 2
+
+#define GSP_ASP_SAVE_MASK_HIDDEN 0x0001
+#define GSP_ASP_SAVE_MASK_ENABLED 0x0002
+#define GSP_ASP_SAVE_MASK_NAME 0x0004
+#define GSP_ASP_SAVE_MASK_EXEC 0x0008
+#define GSP_ASP_SAVE_MASK_COMMENT 0x0010
+#define GSP_ASP_SAVE_MASK_ALL 0xffff
+
+struct _GspAppPrivate {
+ char *basename;
+ char *path;
+
+ gboolean hidden;
+ gboolean enabled;
+
+ char *name;
+ char *exec;
+ char *comment;
+ char *icon;
+
+ GIcon *gicon;
+ char *description;
+
+ /* position of the directory in the XDG environment variable */
+ unsigned int xdg_position;
+ /* position of the first system directory in the XDG env var containing
+ * this autostart app too (G_MAXUINT means none) */
+ unsigned int xdg_system_position;
+
+ unsigned int save_timeout;
+ /* mask of what has changed */
+ unsigned int save_mask;
+ /* path that contains the original file that needs to be saved */
+ char *old_system_path;
+ /* after writing to file, we skip the next file monitor event of type
+ * CHANGED */
+ gboolean skip_next_monitor_event;
+};
+
+#define GSP_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP, GspAppPrivate))
+
+
+enum {
+ CHANGED,
+ REMOVED,
+ LAST_SIGNAL
+};
+
+static guint gsp_app_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GspApp, gsp_app, G_TYPE_OBJECT)
+
+static void gsp_app_dispose (GObject *object);
+static void gsp_app_finalize (GObject *object);
+static gboolean _gsp_app_save (gpointer data);
+
+
+static gboolean
+_gsp_str_equal (const char *a,
+ const char *b)
+{
+ if (g_strcmp0 (a, b) == 0) {
+ return TRUE;
+ }
+
+ if (a && !b && a[0] == '\0') {
+ return TRUE;
+ }
+
+ if (b && !a && b[0] == '\0') {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+gsp_app_class_init (GspAppClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->dispose = gsp_app_dispose;
+ gobject_class->finalize = gsp_app_finalize;
+
+ gsp_app_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GspAppClass,
+ changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gsp_app_signals[REMOVED] =
+ g_signal_new ("removed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GspAppClass,
+ removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (class, sizeof (GspAppPrivate));
+}
+
+static void
+gsp_app_init (GspApp *app)
+{
+ app->priv = GSP_APP_GET_PRIVATE (app);
+
+ memset (app->priv, 0, sizeof (GspAppPrivate));
+ app->priv->xdg_position = G_MAXUINT;
+ app->priv->xdg_system_position = G_MAXUINT;
+}
+
+static void
+_gsp_app_free_reusable_data (GspApp *app)
+{
+ if (app->priv->path) {
+ g_free (app->priv->path);
+ app->priv->path = NULL;
+ }
+
+ if (app->priv->name) {
+ g_free (app->priv->name);
+ app->priv->name = NULL;
+ }
+
+ if (app->priv->exec) {
+ g_free (app->priv->exec);
+ app->priv->exec = NULL;
+ }
+
+ if (app->priv->comment) {
+ g_free (app->priv->comment);
+ app->priv->comment = NULL;
+ }
+
+ if (app->priv->icon) {
+ g_free (app->priv->icon);
+ app->priv->icon = NULL;
+ }
+
+ if (app->priv->gicon) {
+ g_object_unref (app->priv->gicon);
+ app->priv->gicon = NULL;
+ }
+
+ if (app->priv->description) {
+ g_free (app->priv->description);
+ app->priv->description = NULL;
+ }
+
+ if (app->priv->old_system_path) {
+ g_free (app->priv->old_system_path);
+ app->priv->old_system_path = NULL;
+ }
+}
+
+static void
+gsp_app_dispose (GObject *object)
+{
+ GspApp *app;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSP_IS_APP (object));
+
+ app = GSP_APP (object);
+
+ /* we save in dispose since we might need to reference GspAppManager */
+ if (app->priv->save_timeout) {
+ g_source_remove (app->priv->save_timeout);
+ app->priv->save_timeout = 0;
+
+ /* save now */
+ _gsp_app_save (app);
+ }
+
+ G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object);
+}
+
+static void
+gsp_app_finalize (GObject *object)
+{
+ GspApp *app;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSP_IS_APP (object));
+
+ app = GSP_APP (object);
+
+ if (app->priv->basename) {
+ g_free (app->priv->basename);
+ app->priv->basename = NULL;
+ }
+
+ _gsp_app_free_reusable_data (app);
+
+ G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object);
+}
+
+static void
+_gsp_app_emit_changed (GspApp *app)
+{
+ g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0);
+}
+
+static void
+_gsp_app_emit_removed (GspApp *app)
+{
+ g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0);
+}
+
+static void
+_gsp_app_update_description (GspApp *app)
+{
+ const char *primary;
+ const char *secondary;
+
+ if (!gsm_util_text_is_blank (app->priv->name)) {
+ primary = app->priv->name;
+ } else if (!gsm_util_text_is_blank (app->priv->exec)) {
+ primary = app->priv->exec;
+ } else {
+ primary = _("No name");
+ }
+
+ if (!gsm_util_text_is_blank (app->priv->comment)) {
+ secondary = app->priv->comment;
+ } else {
+ secondary = _("No description");
+ }
+
+ g_free (app->priv->description);
+ app->priv->description = g_markup_printf_escaped ("<b>%s</b>\n%s",
+ primary,
+ secondary);
+}
+
+/*
+ * Saving
+ */
+
+static void
+_gsp_ensure_user_autostart_dir (void)
+{
+ char *dir;
+
+ dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
+ g_mkdir_with_parents (dir, S_IRWXU);
+
+ g_free (dir);
+}
+
+static gboolean
+_gsp_app_user_equal_system (GspApp *app,
+ char **system_path)
+{
+ GspAppManager *manager;
+ const char *system_dir;
+ char *path;
+ char *str;
+ GKeyFile *keyfile;
+
+ manager = gsp_app_manager_get ();
+ system_dir = gsp_app_manager_get_dir (manager,
+ app->priv->xdg_system_position);
+ g_object_unref (manager);
+ if (!system_dir) {
+ return FALSE;
+ }
+
+ path = g_build_filename (system_dir, app->priv->basename, NULL);
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+
+ if (gsp_key_file_get_boolean (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+ FALSE) != app->priv->hidden ||
+ gsp_key_file_get_boolean (keyfile,
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+ TRUE) != app->priv->enabled) {
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+
+ str = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_NAME);
+ if (!_gsp_str_equal (str, app->priv->name)) {
+ g_free (str);
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+ g_free (str);
+
+ str = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_COMMENT);
+ if (!_gsp_str_equal (str, app->priv->comment)) {
+ g_free (str);
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+ g_free (str);
+
+ str = gsp_key_file_get_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_EXEC);
+ if (!_gsp_str_equal (str, app->priv->exec)) {
+ g_free (str);
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+ g_free (str);
+
+ str = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_ICON);
+ if (!_gsp_str_equal (str, app->priv->icon)) {
+ g_free (str);
+ g_free (path);
+ g_key_file_free (keyfile);
+ return FALSE;
+ }
+ g_free (str);
+
+ g_key_file_free (keyfile);
+
+ *system_path = path;
+
+ return TRUE;
+}
+
+static inline void
+_gsp_app_save_done_success (GspApp *app)
+{
+ app->priv->save_mask = 0;
+
+ if (app->priv->old_system_path) {
+ g_free (app->priv->old_system_path);
+ app->priv->old_system_path = NULL;
+ }
+}
+
+static gboolean
+_gsp_app_save (gpointer data)
+{
+ GspApp *app;
+ char *use_path;
+ GKeyFile *keyfile;
+ GError *error;
+
+ app = GSP_APP (data);
+
+ /* first check if removing the data from the user dir and using the
+ * data from the system dir is enough -- this helps us keep clean the
+ * user config dir by removing unneeded files */
+ if (_gsp_app_user_equal_system (app, &use_path)) {
+ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
+ g_remove (app->priv->path);
+ }
+
+ g_free (app->priv->path);
+ app->priv->path = use_path;
+
+ app->priv->xdg_position = app->priv->xdg_system_position;
+
+ _gsp_app_save_done_success (app);
+ return FALSE;
+ }
+
+ if (app->priv->old_system_path)
+ use_path = app->priv->old_system_path;
+ else
+ use_path = app->priv->path;
+
+ keyfile = g_key_file_new ();
+
+ error = NULL;
+ g_key_file_load_from_file (keyfile, use_path,
+ G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
+ &error);
+
+ if (error) {
+ g_error_free (error);
+ gsp_key_file_populate (keyfile);
+ }
+
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) {
+ gsp_key_file_set_boolean (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+ app->priv->hidden);
+ }
+
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_ENABLED) {
+ gsp_key_file_set_boolean (keyfile,
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+ app->priv->enabled);
+ }
+
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NAME) {
+ gsp_key_file_set_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ app->priv->name);
+ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME);
+ }
+
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) {
+ gsp_key_file_set_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_COMMENT,
+ app->priv->comment);
+ gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT);
+ }
+
+ if (app->priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) {
+ gsp_key_file_set_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ app->priv->exec);
+ }
+
+ _gsp_ensure_user_autostart_dir ();
+ if (gsp_key_file_to_file (keyfile, app->priv->path, NULL)) {
+ app->priv->skip_next_monitor_event = TRUE;
+ _gsp_app_save_done_success (app);
+ } else {
+ g_warning ("Could not save %s file", app->priv->path);
+ }
+
+ g_key_file_free (keyfile);
+
+ app->priv->save_timeout = 0;
+ return FALSE;
+}
+
+static void
+_gsp_app_queue_save (GspApp *app)
+{
+ if (app->priv->save_timeout) {
+ g_source_remove (app->priv->save_timeout);
+ app->priv->save_timeout = 0;
+ }
+
+ /* if the file was not in the user directory, then we'll create a copy
+ * there */
+ if (app->priv->xdg_position != 0) {
+ app->priv->xdg_position = 0;
+
+ if (app->priv->old_system_path == NULL) {
+ app->priv->old_system_path = app->priv->path;
+ /* if old_system_path was not NULL, then it means we
+ * tried to save and we failed; in that case, we want
+ * to try again and use the old file as a basis again */
+ }
+
+ app->priv->path = g_build_filename (g_get_user_config_dir (),
+ "autostart",
+ app->priv->basename, NULL);
+ }
+
+ app->priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY,
+ _gsp_app_save,
+ app);
+}
+
+/*
+ * Accessors
+ */
+
+const char *
+gsp_app_get_basename (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->basename;
+}
+
+const char *
+gsp_app_get_path (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->path;
+}
+
+gboolean
+gsp_app_get_hidden (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+ return app->priv->hidden;
+}
+
+gboolean
+gsp_app_get_enabled (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), FALSE);
+
+ return app->priv->enabled;
+}
+
+void
+gsp_app_set_enabled (GspApp *app,
+ gboolean enabled)
+{
+ g_return_if_fail (GSP_IS_APP (app));
+
+ if (enabled == app->priv->enabled) {
+ return;
+ }
+
+ app->priv->enabled = enabled;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
+
+ _gsp_app_queue_save (app);
+ _gsp_app_emit_changed (app);
+}
+
+const char *
+gsp_app_get_name (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->name;
+}
+
+const char *
+gsp_app_get_exec (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->exec;
+}
+
+const char *
+gsp_app_get_comment (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->comment;
+}
+
+GIcon *
+gsp_app_get_icon (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ if (app->priv->gicon) {
+ return g_object_ref (app->priv->gicon);
+ } else {
+ return NULL;
+ }
+}
+
+unsigned int
+gsp_app_get_xdg_position (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
+
+ return app->priv->xdg_position;
+}
+
+unsigned int
+gsp_app_get_xdg_system_position (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT);
+
+ return app->priv->xdg_system_position;
+}
+
+void
+gsp_app_set_xdg_system_position (GspApp *app,
+ unsigned int position)
+{
+ g_return_if_fail (GSP_IS_APP (app));
+
+ app->priv->xdg_system_position = position;
+}
+
+const char *
+gsp_app_get_description (GspApp *app)
+{
+ g_return_val_if_fail (GSP_IS_APP (app), NULL);
+
+ return app->priv->description;
+}
+
+/*
+ * High-level edition
+ */
+
+void
+gsp_app_update (GspApp *app,
+ const char *name,
+ const char *comment,
+ const char *exec)
+{
+ gboolean changed;
+
+ g_return_if_fail (GSP_IS_APP (app));
+
+ changed = FALSE;
+
+ if (!_gsp_str_equal (name, app->priv->name)) {
+ changed = TRUE;
+ g_free (app->priv->name);
+ app->priv->name = g_strdup (name);
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME;
+ }
+
+ if (!_gsp_str_equal (comment, app->priv->comment)) {
+ changed = TRUE;
+ g_free (app->priv->comment);
+ app->priv->comment = g_strdup (comment);
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT;
+ }
+
+ if (changed) {
+ _gsp_app_update_description (app);
+ }
+
+ if (!_gsp_str_equal (exec, app->priv->exec)) {
+ changed = TRUE;
+ g_free (app->priv->exec);
+ app->priv->exec = g_strdup (exec);
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC;
+ }
+
+ if (changed) {
+ _gsp_app_queue_save (app);
+ _gsp_app_emit_changed (app);
+ }
+}
+
+void
+gsp_app_delete (GspApp *app)
+{
+ g_return_if_fail (GSP_IS_APP (app));
+
+ if (app->priv->xdg_position == 0 &&
+ app->priv->xdg_system_position == G_MAXUINT) {
+ /* exists in user directory only */
+ if (app->priv->save_timeout) {
+ g_source_remove (app->priv->save_timeout);
+ app->priv->save_timeout = 0;
+ }
+
+ if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) {
+ g_remove (app->priv->path);
+ }
+
+ /* for extra safety */
+ app->priv->hidden = TRUE;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+
+ _gsp_app_emit_removed (app);
+ } else {
+ /* also exists in system directory, so we have to keep a file
+ * in the user directory */
+ app->priv->hidden = TRUE;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+
+ _gsp_app_queue_save (app);
+ _gsp_app_emit_changed (app);
+ }
+}
+
+/*
+ * New autostart app
+ */
+
+void
+gsp_app_reload_at (GspApp *app,
+ const char *path,
+ unsigned int xdg_position)
+{
+ g_return_if_fail (GSP_IS_APP (app));
+
+ app->priv->xdg_position = G_MAXUINT;
+ gsp_app_new (path, xdg_position);
+}
+
+GspApp *
+gsp_app_new (const char *path,
+ unsigned int xdg_position)
+{
+ GspAppManager *manager;
+ GspApp *app;
+ GKeyFile *keyfile;
+ char *basename;
+ gboolean new;
+
+ basename = g_path_get_basename (path);
+
+ manager = gsp_app_manager_get ();
+ app = gsp_app_manager_find_app_with_basename (manager, basename);
+ g_object_unref (manager);
+
+ new = (app == NULL);
+
+ if (!new) {
+ if (app->priv->xdg_position == xdg_position) {
+ if (app->priv->skip_next_monitor_event) {
+ app->priv->skip_next_monitor_event = FALSE;
+ return NULL;
+ }
+ /* else: the file got changed but not by us, we'll
+ * update our data from disk */
+ }
+
+ if (app->priv->xdg_position < xdg_position ||
+ app->priv->save_timeout != 0) {
+ /* we don't really care about this file, since we
+ * already have something with a higher priority, or
+ * we're going to write something in the user config
+ * anyway.
+ * Note: xdg_position >= 1 so it's a system dir */
+ app->priv->xdg_system_position = MIN (xdg_position,
+ app->priv->xdg_system_position);
+ return NULL;
+ }
+ }
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) {
+ g_key_file_free (keyfile);
+ g_free (basename);
+ return NULL;
+ }
+
+ if (new) {
+ app = g_object_new (GSP_TYPE_APP, NULL);
+ app->priv->basename = basename;
+ } else {
+ g_free (basename);
+ _gsp_app_free_reusable_data (app);
+ }
+
+ app->priv->path = g_strdup (path);
+
+ app->priv->hidden = gsp_key_file_get_boolean (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+ FALSE);
+ app->priv->enabled = gsp_key_file_get_boolean (keyfile,
+ GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED,
+ TRUE);
+
+ app->priv->name = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_NAME);
+ app->priv->exec = gsp_key_file_get_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_EXEC);
+ app->priv->comment = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_COMMENT);
+
+ if (gsm_util_text_is_blank (app->priv->name)) {
+ g_free (app->priv->name);
+ app->priv->name = g_strdup (app->priv->exec);
+ }
+
+ app->priv->icon = gsp_key_file_get_locale_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_ICON);
+
+ if (app->priv->icon) {
+ /* look at icon and see if it's a themed icon or not */
+ if (g_path_is_absolute (app->priv->icon)) {
+ GFile *iconfile;
+
+ iconfile = g_file_new_for_path (app->priv->icon);
+ app->priv->gicon = g_file_icon_new (iconfile);
+ g_object_unref (iconfile);
+ } else {
+ app->priv->gicon = g_themed_icon_new (app->priv->icon);
+ }
+ } else {
+ app->priv->gicon = NULL;
+ }
+
+ g_key_file_free (keyfile);
+
+ _gsp_app_update_description (app);
+
+ if (xdg_position > 0) {
+ g_assert (xdg_position <= app->priv->xdg_system_position);
+ app->priv->xdg_system_position = xdg_position;
+ }
+ /* else we keep the old value (which is G_MAXUINT if it wasn't set) */
+ app->priv->xdg_position = xdg_position;
+
+ g_assert (!new || app->priv->save_timeout == 0);
+ app->priv->save_timeout = 0;
+ app->priv->old_system_path = NULL;
+ app->priv->skip_next_monitor_event = FALSE;
+
+ if (!new) {
+ _gsp_app_emit_changed (app);
+ }
+
+ return app;
+}
+
+static char *
+_gsp_find_free_basename (const char *suggested_basename)
+{
+ GspAppManager *manager;
+ char *base_path;
+ char *filename;
+ char *basename;
+ int i;
+
+ if (g_str_has_suffix (suggested_basename, ".desktop")) {
+ char *basename_no_ext;
+
+ basename_no_ext = g_strndup (suggested_basename,
+ strlen (suggested_basename) - strlen (".desktop"));
+ base_path = g_build_filename (g_get_user_config_dir (),
+ "autostart",
+ basename_no_ext, NULL);
+ g_free (basename_no_ext);
+ } else {
+ base_path = g_build_filename (g_get_user_config_dir (),
+ "autostart",
+ suggested_basename, NULL);
+ }
+
+ filename = g_strdup_printf ("%s.desktop", base_path);
+ basename = g_path_get_basename (filename);
+
+ manager = gsp_app_manager_get ();
+
+ i = 1;
+#define _GSP_FIND_MAX_TRY 10000
+ while (gsp_app_manager_find_app_with_basename (manager,
+ basename) != NULL &&
+ g_file_test (filename, G_FILE_TEST_EXISTS) &&
+ i < _GSP_FIND_MAX_TRY) {
+ g_free (filename);
+ g_free (basename);
+
+ filename = g_strdup_printf ("%s-%d.desktop", base_path, i);
+ basename = g_path_get_basename (filename);
+
+ i++;
+ }
+
+ g_object_unref (manager);
+
+ g_free (base_path);
+ g_free (filename);
+
+ if (i == _GSP_FIND_MAX_TRY) {
+ g_free (basename);
+ return NULL;
+ }
+
+ return basename;
+}
+
+void
+gsp_app_create (const char *name,
+ const char *comment,
+ const char *exec)
+{
+ GspAppManager *manager;
+ GspApp *app;
+ char *basename;
+ char **argv;
+ int argc;
+
+ g_return_if_fail (!gsm_util_text_is_blank (exec));
+
+ if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) {
+ return;
+ }
+
+ basename = _gsp_find_free_basename (argv[0]);
+ g_strfreev (argv);
+ if (basename == NULL) {
+ return;
+ }
+
+ app = g_object_new (GSP_TYPE_APP, NULL);
+
+ app->priv->basename = basename;
+ app->priv->path = g_build_filename (g_get_user_config_dir (),
+ "autostart",
+ app->priv->basename, NULL);
+
+ app->priv->hidden = FALSE;
+ app->priv->enabled = TRUE;
+
+ if (!gsm_util_text_is_blank (name)) {
+ app->priv->name = g_strdup (name);
+ } else {
+ app->priv->name = g_strdup (exec);
+ }
+ app->priv->exec = g_strdup (exec);
+ app->priv->comment = g_strdup (comment);
+ app->priv->icon = NULL;
+
+ app->priv->gicon = NULL;
+ _gsp_app_update_description (app);
+
+ /* by definition */
+ app->priv->xdg_position = 0;
+ app->priv->xdg_system_position = G_MAXUINT;
+
+ app->priv->save_timeout = 0;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ALL;
+ app->priv->old_system_path = NULL;
+ app->priv->skip_next_monitor_event = FALSE;
+
+ _gsp_app_queue_save (app);
+
+ manager = gsp_app_manager_get ();
+ gsp_app_manager_add (manager, app);
+ g_object_unref (app);
+ g_object_unref (manager);
+}
+
+gboolean
+gsp_app_copy_desktop_file (const char *uri)
+{
+ GspAppManager *manager;
+ GspApp *app;
+ GFile *src_file;
+ char *src_basename;
+ char *dst_basename;
+ char *dst_path;
+ GFile *dst_file;
+ gboolean changed;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ src_file = g_file_new_for_uri (uri);
+ src_basename = g_file_get_basename (src_file);
+
+ if (src_basename == NULL) {
+ g_object_unref (src_file);
+ return FALSE;
+ }
+
+ dst_basename = _gsp_find_free_basename (src_basename);
+ g_free (src_basename);
+
+ if (dst_basename == NULL) {
+ g_object_unref (src_file);
+ return FALSE;
+ }
+
+ dst_path = g_build_filename (g_get_user_config_dir (),
+ "autostart",
+ dst_basename, NULL);
+ g_free (dst_basename);
+
+ dst_file = g_file_new_for_path (dst_path);
+
+ _gsp_ensure_user_autostart_dir ();
+ if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
+ NULL, NULL, NULL, NULL)) {
+ g_object_unref (src_file);
+ g_object_unref (dst_file);
+ g_free (dst_path);
+ return FALSE;
+ }
+
+ g_object_unref (src_file);
+ g_object_unref (dst_file);
+
+ app = gsp_app_new (dst_path, 0);
+ if (!app) {
+ g_remove (dst_path);
+ g_free (dst_path);
+ return FALSE;
+ }
+
+ g_free (dst_path);
+
+ changed = FALSE;
+ if (app->priv->hidden) {
+ changed = TRUE;
+ app->priv->hidden = FALSE;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN;
+ }
+
+ if (!app->priv->enabled) {
+ changed = TRUE;
+ app->priv->enabled = TRUE;
+ app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED;
+ }
+
+ if (changed) {
+ _gsp_app_queue_save (app);
+ }
+
+ manager = gsp_app_manager_get ();
+ gsp_app_manager_add (manager, app);
+ g_object_unref (app);
+ g_object_unref (manager);
+
+ return TRUE;
+}
diff --git a/capplet/gsp-app.h b/capplet/gsp-app.h
new file mode 100644
index 0000000..f869cf3
--- /dev/null
+++ b/capplet/gsp-app.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2009 Vincent Untz.
+ * Copyright (C) 2008 Lucas Rocha.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GSP_APP_H
+#define __GSP_APP_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GSP_TYPE_APP (gsp_app_get_type ())
+#define GSP_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP, GspApp))
+#define GSP_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP, GspAppClass))
+#define GSP_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP))
+#define GSP_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP))
+#define GSP_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP, GspAppClass))
+
+typedef struct _GspApp GspApp;
+typedef struct _GspAppClass GspAppClass;
+
+typedef struct _GspAppPrivate GspAppPrivate;
+
+struct _GspAppClass
+{
+ GObjectClass parent_class;
+
+ void (* changed) (GspApp *app);
+ void (* removed) (GspApp *app);
+};
+
+struct _GspApp
+{
+ GObject parent_instance;
+
+ GspAppPrivate *priv;
+};
+
+GType gsp_app_get_type (void);
+
+void gsp_app_create (const char *name,
+ const char *comment,
+ const char *exec);
+void gsp_app_update (GspApp *app,
+ const char *name,
+ const char *comment,
+ const char *exec);
+
+gboolean gsp_app_copy_desktop_file (const char *uri);
+
+void gsp_app_delete (GspApp *app);
+
+const char *gsp_app_get_basename (GspApp *app);
+const char *gsp_app_get_path (GspApp *app);
+
+gboolean gsp_app_get_hidden (GspApp *app);
+
+gboolean gsp_app_get_enabled (GspApp *app);
+void gsp_app_set_enabled (GspApp *app,
+ gboolean enabled);
+
+const char *gsp_app_get_name (GspApp *app);
+const char *gsp_app_get_exec (GspApp *app);
+const char *gsp_app_get_comment (GspApp *app);
+
+const char *gsp_app_get_description (GspApp *app);
+GIcon *gsp_app_get_icon (GspApp *app);
+
+/* private interface for GspAppManager only */
+
+GspApp *gsp_app_new (const char *path,
+ unsigned int xdg_position);
+
+void gsp_app_reload_at (GspApp *app,
+ const char *path,
+ unsigned int xdg_position);
+
+unsigned int gsp_app_get_xdg_position (GspApp *app);
+unsigned int gsp_app_get_xdg_system_position (GspApp *app);
+void gsp_app_set_xdg_system_position (GspApp *app,
+ unsigned int position);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GSP_APP_H */
diff --git a/capplet/gsp-keyfile.c b/capplet/gsp-keyfile.c
new file mode 100644
index 0000000..405ecc0
--- /dev/null
+++ b/capplet/gsp-keyfile.c
@@ -0,0 +1,149 @@
+/*
+ * gsp-keyfile.c: GKeyFile extensions
+ *
+ * Copyright (C) 2008, 2009 Novell, Inc.
+ *
+ * Based on code from panel-keyfile.c (from mate-panel)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors:
+ * Vincent Untz <[email protected]>
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "gsp-keyfile.h"
+
+void
+gsp_key_file_populate (GKeyFile *keyfile)
+{
+ gsp_key_file_set_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_TYPE,
+ "Application");
+
+ gsp_key_file_set_string (keyfile,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ "/bin/false");
+}
+
+//FIXME: kill this when bug #309224 is fixed
+gboolean
+gsp_key_file_to_file (GKeyFile *keyfile,
+ const gchar *path,
+ GError **error)
+{
+ GError *write_error;
+ gchar *data;
+ gsize length;
+ gboolean res;
+
+ g_return_val_if_fail (keyfile != NULL, FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ write_error = NULL;
+ data = g_key_file_to_data (keyfile, &length, &write_error);
+
+ if (write_error) {
+ g_propagate_error (error, write_error);
+ return FALSE;
+ }
+
+ res = g_file_set_contents (path, data, length, &write_error);
+ g_free (data);
+
+ if (write_error) {
+ g_propagate_error (error, write_error);
+ return FALSE;
+ }
+
+ return res;
+}
+
+gboolean
+gsp_key_file_get_boolean (GKeyFile *keyfile,
+ const gchar *key,
+ gboolean default_value)
+{
+ GError *error;
+ gboolean retval;
+
+ error = NULL;
+ retval = g_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ key, &error);
+ if (error != NULL) {
+ retval = default_value;
+ g_error_free (error);
+ }
+
+ return retval;
+}
+
+void
+gsp_key_file_set_locale_string (GKeyFile *keyfile,
+ const gchar *key,
+ const gchar *value)
+{
+ const char *locale;
+ const char * const *langs_pointer;
+ int i;
+
+ if (value == NULL) {
+ value = "";
+ }
+
+ locale = NULL;
+ langs_pointer = g_get_language_names ();
+ for (i = 0; langs_pointer[i] != NULL; i++) {
+ /* find first without encoding */
+ if (strchr (langs_pointer[i], '.') == NULL) {
+ locale = langs_pointer[i];
+ break;
+ }
+ }
+
+ if (locale != NULL) {
+ g_key_file_set_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ key, locale, value);
+ } else {
+ g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ key, value);
+ }
+}
+
+void
+gsp_key_file_ensure_C_key (GKeyFile *keyfile,
+ const char *key)
+{
+ char *C_value;
+ char *buffer;
+
+ /* Make sure we set the "C" locale strings to the terms we set here.
+ * This is so that if the user logs into another locale they get their
+ * own description there rather then empty. It is not the C locale
+ * however, but the user created this entry herself so it's OK */
+ C_value = gsp_key_file_get_string (keyfile, key);
+ if (C_value == NULL || C_value [0] == '\0') {
+ buffer = gsp_key_file_get_locale_string (keyfile, key);
+ if (buffer) {
+ gsp_key_file_set_string (keyfile, key, buffer);
+ g_free (buffer);
+ }
+ }
+ g_free (C_value);
+}
diff --git a/capplet/gsp-keyfile.h b/capplet/gsp-keyfile.h
new file mode 100644
index 0000000..42b5c6e
--- /dev/null
+++ b/capplet/gsp-keyfile.h
@@ -0,0 +1,67 @@
+/*
+ * gsp-keyfile.h: GKeyFile extensions
+ *
+ * Copyright (C) 2008, 2009 Novell, Inc.
+ *
+ * Based on code from panel-keyfile.h (from mate-panel)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors:
+ * Vincent Untz <[email protected]>
+ */
+
+#ifndef GSP_KEYFILE_H
+#define GSP_KEYFILE_H
+
+#include "glib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED "X-MATE-Autostart-enabled"
+
+void gsp_key_file_populate (GKeyFile *keyfile);
+
+gboolean gsp_key_file_to_file (GKeyFile *keyfile,
+ const gchar *path,
+ GError **error);
+
+gboolean gsp_key_file_get_boolean (GKeyFile *keyfile,
+ const gchar *key,
+ gboolean default_value);
+#define gsp_key_file_get_string(key_file, key) \
+ g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL)
+#define gsp_key_file_get_locale_string(key_file, key) \
+ g_key_file_get_locale_string(key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL)
+
+#define gsp_key_file_set_boolean(key_file, key, value) \
+ g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
+#define gsp_key_file_set_string(key_file, key, value) \
+ g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value)
+void gsp_key_file_set_locale_string (GKeyFile *keyfile,
+ const gchar *key,
+ const gchar *value);
+
+void gsp_key_file_ensure_C_key (GKeyFile *keyfile,
+ const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GSP_KEYFILE_H */
diff --git a/capplet/main.c b/capplet/main.c
new file mode 100644
index 0000000..155b560
--- /dev/null
+++ b/capplet/main.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * main.c
+ * Copyright (C) 1999 Free Software Foundation, Inc.
+ * Copyright (C) 2008 Lucas Rocha.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <mateconf/mateconf-client.h>
+
+#include "gsm-properties-dialog.h"
+
+static gboolean show_version = FALSE;
+
+static GOptionEntry options[] = {
+ {"version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL},
+ {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+static void dialog_response(GsmPropertiesDialog* dialog, guint response_id, gpointer data)
+{
+ GdkScreen* screen;
+ GError* error;
+
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ screen = gtk_widget_get_screen(GTK_WIDGET (dialog));
+
+ error = NULL;
+ gtk_show_uri (screen, "ghelp:user-guide?gosstartsession-2",
+ gtk_get_current_event_time (), &error);
+
+ if (error != NULL)
+ {
+ GtkWidget* d = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", _("Could not display help document"));
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(d), "%s", error->message);
+ g_error_free(error);
+
+ gtk_dialog_run(GTK_DIALOG (d));
+ gtk_widget_destroy(d);
+ }
+ }
+ else
+ {
+ gtk_widget_destroy(GTK_WIDGET (dialog));
+ gtk_main_quit();
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ GError* error;
+ GtkWidget* dialog;
+
+ bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+ textdomain(GETTEXT_PACKAGE);
+
+ error = NULL;
+
+ if (!gtk_init_with_args(&argc, &argv, " - MATE Session Properties", options, GETTEXT_PACKAGE, &error))
+ {
+ g_warning("Unable to start: %s", error->message);
+ g_error_free(error);
+ return 1;
+ }
+
+ if (show_version)
+ {
+ g_print("%s %s\n", argv[0], VERSION);
+ return 0;
+ }
+
+ dialog = gsm_properties_dialog_new();
+ g_signal_connect(dialog, "response", G_CALLBACK(dialog_response), NULL);
+ gtk_widget_show(dialog);
+
+ gtk_main();
+
+ return 0;
+}