diff options
Diffstat (limited to 'capplet')
-rw-r--r-- | capplet/Makefile.am | 33 | ||||
-rw-r--r-- | capplet/Makefile.in | 639 | ||||
-rw-r--r-- | capplet/gsm-app-dialog.c | 544 | ||||
-rw-r--r-- | capplet/gsm-app-dialog.h | 70 | ||||
-rw-r--r-- | capplet/gsm-properties-dialog.c | 819 | ||||
-rw-r--r-- | capplet/gsm-properties-dialog.h | 61 | ||||
-rw-r--r-- | capplet/gsp-app-manager.c | 593 | ||||
-rw-r--r-- | capplet/gsp-app-manager.h | 85 | ||||
-rw-r--r-- | capplet/gsp-app.c | 1050 | ||||
-rw-r--r-- | capplet/gsp-app.h | 109 | ||||
-rw-r--r-- | capplet/gsp-keyfile.c | 149 | ||||
-rw-r--r-- | capplet/gsp-keyfile.h | 67 | ||||
-rw-r--r-- | capplet/main.c | 102 |
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. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +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 = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(mate_session_properties_SOURCES) +DIST_SOURCES = $(mate_session_properties_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@ +DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@ +DEFAULT_WM = @DEFAULT_WM@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISABLE_DEPRECATED_CFLAGS = @DISABLE_DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGG_SMCLIENT_CFLAGS = @EGG_SMCLIENT_CFLAGS@ +EGG_SMCLIENT_LIBS = @EGG_SMCLIENT_LIBS@ +EGREP = @EGREP@ +EXECINFO_LIBS = @EXECINFO_LIBS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +HAVE_XRENDER = @HAVE_XRENDER@ +HAVE_XTEST = @HAVE_XTEST@ +ICE_CFLAGS = @ICE_CFLAGS@ +ICE_LIBS = @ICE_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_CFLAGS = @MATECONF_CFLAGS@ +MATECONF_LIBS = @MATECONF_LIBS@ +MATECONF_SANITY_CHECK = @MATECONF_SANITY_CHECK@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_SESSION_CFLAGS = @MATE_SESSION_CFLAGS@ +MATE_SESSION_LIBS = @MATE_SESSION_LIBS@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +REBUILD = @REBUILD@ +SED = @SED@ +SESSION_PROPERTIES_CFLAGS = @SESSION_PROPERTIES_CFLAGS@ +SESSION_PROPERTIES_LIBS = @SESSION_PROPERTIES_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SM_CFLAGS = @SM_CFLAGS@ +SM_LIBS = @SM_LIBS@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XEXT_CFLAGS = @XEXT_CFLAGS@ +XEXT_LIBS = @XEXT_LIBS@ +XGETTEXT = @XGETTEXT@ +XMKMF = @XMKMF@ +XMLTO = @XMLTO@ +XRENDER_CFLAGS = @XRENDER_CFLAGS@ +XRENDER_LIBS = @XRENDER_LIBS@ +XSLTPROC = @XSLTPROC@ +XTEST_CFLAGS = @XTEST_CFLAGS@ +XTEST_LIBS = @XTEST_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +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: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu 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)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-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 + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsm-app-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsm-properties-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsp-app-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsp-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsp-keyfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: 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; +} |